cb21075d |
1 | // Socket.C -*- C++ -*- |
439869bf |
2 | // Copyright (C) 2002 Clinton Ebadi |
cb21075d |
3 | // Copyright (c) 1997, 1998 Etienne BERNARD |
4 | |
5 | // This program is free software; you can redistribute it and/or modify |
6 | // it under the terms of the GNU General Public License as published by |
7 | // the Free Software Foundation; either version 2 of the License, or |
8 | // any later version. |
9 | |
10 | // This program is distributed in the hope that it will be useful, |
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | // GNU General Public License for more details. |
14 | |
15 | // You should have received a copy of the GNU General Public License |
16 | // along with this program; if not, write to the Free Software |
17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. |
18 | |
19 | #include "Socket.H" |
fd7440f1 |
20 | #include <string> |
cb21075d |
21 | |
22 | #include <sys/types.h> |
23 | #include <sys/socket.h> |
24 | #include <netdb.h> |
25 | #include <netinet/in.h> |
26 | #include <arpa/inet.h> |
27 | #include <string.h> |
28 | #include <unistd.h> |
29 | #include <fcntl.h> |
30 | #include <errno.h> |
31 | |
32 | Socket::Socket() |
33 | : remotePort(0), remoteAddress(0), |
34 | localPort(0), localAddress(0) |
35 | { |
36 | fd = new s_fd; |
37 | fd->fd = socket(AF_INET, SOCK_STREAM, 0); |
38 | } |
39 | |
40 | Socket::Socket(int newfd, unsigned long newAddress, int newPort) |
41 | : remotePort(newPort), remoteAddress(newAddress), |
42 | localPort(0), localAddress(0) |
43 | { |
44 | fd = new s_fd; |
45 | fd->fd = newfd; |
46 | } |
47 | |
48 | Socket::Socket(const Socket &s) |
49 | : remotePort(s.remotePort), remoteAddress(s.remoteAddress), |
50 | localPort(s.localPort), localAddress(s.localAddress) |
51 | { |
52 | s.fd->n++; |
53 | fd = s.fd; |
54 | } |
55 | |
56 | Socket::~Socket() |
57 | { |
58 | if (--fd->n == 0) { |
59 | close(); |
60 | delete fd; |
61 | } |
62 | } |
63 | |
64 | int |
65 | Socket::getRemotePort() const |
66 | { |
67 | return remotePort; |
68 | } |
69 | |
70 | int |
71 | Socket::getLocalPort() const |
72 | { |
73 | return localPort; |
74 | } |
75 | |
76 | int |
77 | Socket::getFileDescriptor() const |
78 | { |
79 | return fd->fd; |
80 | } |
81 | |
82 | bool |
83 | Socket::isConnected() |
84 | { |
85 | return fd->fd != -1; |
86 | } |
87 | |
88 | bool |
89 | Socket::setRemoteHostname(String hostname) |
90 | { |
91 | struct hostent *host; |
92 | |
93 | if ((host = gethostbyname((const char *)hostname)) == 0) |
94 | return false; |
95 | |
96 | memcpy(&remoteAddress, host->h_addr, host->h_length); |
97 | remoteAddress = ntohl(remoteAddress); |
98 | |
99 | return true; |
100 | } |
101 | |
102 | bool |
103 | Socket::setRemoteIP(unsigned long address) |
104 | { |
105 | remoteAddress = address; |
106 | return true; |
107 | } |
108 | |
109 | bool |
110 | Socket::setRemotePort(int p) |
111 | { |
112 | remotePort = p; |
113 | |
114 | return true; |
115 | } |
116 | |
117 | bool |
118 | Socket::setLocalHostname(String hostname) |
119 | { |
120 | struct hostent *host; |
121 | |
122 | if ((host = gethostbyname((const char *)hostname)) == 0) |
123 | return false; |
124 | |
125 | memcpy(&localAddress, host->h_addr, host->h_length); |
126 | |
127 | struct sockaddr_in local_addr; |
128 | local_addr.sin_family = AF_INET; |
129 | local_addr.sin_port = htons(localPort); |
130 | local_addr.sin_addr.s_addr = htonl(localAddress); |
131 | if (bind(fd->fd, (struct sockaddr *)(&local_addr), sizeof(local_addr)) < 0) |
132 | return false; |
133 | |
134 | return true; |
135 | } |
136 | |
137 | bool |
138 | Socket::setLocalIP(unsigned long address) |
139 | { |
140 | localAddress = address; |
141 | |
142 | struct sockaddr_in local_addr; |
143 | local_addr.sin_family = AF_INET; |
144 | local_addr.sin_port = htons(localPort); |
145 | local_addr.sin_addr.s_addr = htonl(localAddress); |
146 | if (bind(fd->fd, (struct sockaddr *)(&local_addr), sizeof(local_addr)) < 0) |
147 | return false; |
148 | |
149 | return true; |
150 | } |
151 | |
152 | bool |
153 | Socket::setLocalPort(int p) |
154 | { |
155 | localPort = p; |
156 | |
157 | struct sockaddr_in local_addr; |
158 | local_addr.sin_family = AF_INET; |
159 | local_addr.sin_port = htons(localPort); |
160 | local_addr.sin_addr.s_addr = htonl(localAddress); |
161 | if (bind(fd->fd, (struct sockaddr *)(&local_addr), sizeof(local_addr)) < 0) |
162 | return false; |
163 | |
164 | return true; |
165 | } |
166 | |
167 | bool |
168 | Socket::setNonBlocking(bool nonblock) |
169 | { |
170 | long flags; |
171 | |
172 | if (fd->fd == -1) |
173 | return false; |
174 | |
175 | // We first get the file descriptor's flags |
176 | if (!fcntl(fd->fd, F_GETFL, &flags)) |
177 | return false; |
178 | |
179 | if (nonblock) |
180 | return fcntl(fd->fd, F_SETFL, flags | O_NONBLOCK); |
181 | else |
182 | return fcntl(fd->fd, F_SETFL, flags & ~O_NONBLOCK); |
183 | } |
184 | |
185 | bool |
186 | Socket::connect() |
187 | { |
188 | struct sockaddr_in addr; |
189 | memset(&addr, 0, sizeof(addr)); |
190 | addr.sin_family = AF_INET; |
191 | addr.sin_addr.s_addr = htonl(remoteAddress); |
192 | addr.sin_port = htons(remotePort); |
193 | if (::connect(fd->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) |
fd7440f1 |
194 | { |
195 | // I'd rather log this to the log file |
196 | std::cerr << strerror (errno) << std::endl; |
197 | return false; |
198 | } |
cb21075d |
199 | |
200 | return true; |
201 | } |
202 | |
203 | bool |
204 | Socket::listen(int backlog) |
205 | { |
206 | return ::listen(fd->fd, backlog) == 0; |
207 | } |
208 | |
209 | Socket |
210 | Socket::accept() |
211 | { |
212 | struct sockaddr_in addr; |
213 | socklen_t addrlen=sizeof(addr); |
214 | int newfd = ::accept(fd->fd, (sockaddr *)&addr, &addrlen); |
215 | if (newfd == -1) |
216 | return Socket(-1, 0, 0); |
217 | unsigned long newRemoteAddress = ntohl(addr.sin_addr.s_addr); |
218 | int newRemotePort = ntohs(addr.sin_port); |
219 | return Socket(newfd, newRemoteAddress, newRemotePort); |
220 | } |
221 | |
222 | void |
223 | Socket::close() |
224 | { |
225 | ::close(fd->fd); |
226 | fd->fd = -1; |
227 | } |
228 | |
229 | bool |
230 | Socket::write(String s, bool m) |
231 | { |
232 | if (fd->fd == -1) |
233 | return false; |
234 | |
235 | if (m) { |
236 | if (::write(fd->fd, (const char *)s, s.length()) + |
237 | ::write(fd->fd, "\n", 1) != s.length() + 1) |
238 | return false; |
239 | } |
240 | else |
241 | if (::write(fd->fd, (const char *)s, s.length()) + |
242 | ::write(fd->fd, "\r\n", 2) != s.length() + 2) |
243 | return false; |
244 | |
245 | return true; |
246 | } |
247 | |
248 | String |
249 | Socket::readLine() |
250 | { |
fd7440f1 |
251 | static std::string buf (512, ' '); |
cb21075d |
252 | int pos = 0, nb; |
253 | char r; |
fd7440f1 |
254 | std::size_t length = buf.length (); |
255 | |
439869bf |
256 | do |
257 | { |
258 | nb = ::read(fd->fd, &r, 1); |
259 | switch (nb) { |
260 | case 0: |
261 | return String(""); |
262 | case -1: |
263 | if (errno != EINTR && errno != EAGAIN) |
264 | return String(""); |
265 | sleep(1); |
266 | } |
267 | |
268 | if (nb != -1) |
fd7440f1 |
269 | if (pos < length) |
270 | buf[pos++] = r; |
271 | else |
272 | { |
273 | buf.resize (length * 2); |
274 | length = buf.length (); |
275 | buf[pos++] = r; |
276 | } |
439869bf |
277 | } while (r != '\n'); |
cb21075d |
278 | |
439869bf |
279 | if (pos > 1 && buf[pos-2] == '\r') |
cb21075d |
280 | buf[pos-2] = '\0'; |
281 | else |
439869bf |
282 | buf[pos-1] = '\0'; |
cb21075d |
283 | |
284 | return String(buf); |
285 | } |
286 | |
287 | String |
288 | Socket::readChar() |
289 | { |
290 | char r[2]; |
291 | int nb; |
292 | nb = ::read(fd->fd, &r, 1); |
293 | switch (nb) { |
294 | case 0: |
295 | return String(""); |
296 | case -1: |
297 | if(errno != EINTR && errno != EAGAIN) |
298 | return String(""); |
299 | sleep(1); |
300 | } |
301 | r[1] = '\0'; |
302 | return String(r); |
303 | } |
304 | |
305 | |
306 | // // Here we use a circular buffer to read from the socket |
307 | // String |
308 | // Socket::readLine() |
309 | // { |
310 | // // The result string |
311 | // char result[512]; |
312 | // // Position in the result string |
313 | // int pos = 0; |
314 | // // Number of chars read from the socket |
315 | // int nb; |
316 | |
317 | // // First, we read from the socket |
318 | // beginning: |
319 | // if (end <= begin) { |
320 | // nb = ::read(fd->fd, buf+end, begin - end); |
321 | // } else { |
322 | // nb = ::read(fd->fd, buf+end, 1024 - end); |
323 | // } |
324 | |
325 | // if (nb == -1 && errno != EINTR && errno != EAGAIN) |
326 | // return String(""); |
327 | |
328 | // end = (end + nb) % 1024; |
329 | |
330 | // // Fine, now we read our string from the buffer |
331 | // while (buf[begin] != '\r' && buf[begin] != '\n') { |
332 | // result[pos++] = buf[begin++]; |
333 | // begin %= 1024; |
334 | // if (begin == end) |
335 | // goto beginning; |
336 | // } |
337 | |
338 | // result[pos] = '\0'; |
339 | |
340 | // // Now we skip the final '\r' and '\n' |
341 | // if (buf[begin] == '\r') |
342 | // begin = (begin + 1) % 1024; |
343 | // if (buf[begin] == '\n') |
344 | // begin = (begin + 1) % 1024; |
345 | |
346 | // // And we return the result |
347 | // return String(result); |
348 | // } |
349 | |
350 | // bool |
351 | // Socket::hasData() |
352 | // { |
353 | // cout << "DEBUG hasData = " << (begin != end) << endl; |
354 | // return begin != end; |
355 | // } |