cb21075d |
1 | // Socket.C -*- C++ -*- |
7b564711 |
2 | // Copyright (C) 2002,2005 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 |
7b564711 |
17 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
18 | // 02110-1301, USA. |
cb21075d |
19 | |
20 | #include "Socket.H" |
cfa82921 |
21 | |
fd7440f1 |
22 | #include <string> |
cb21075d |
23 | |
24 | #include <sys/types.h> |
25 | #include <sys/socket.h> |
26 | #include <netdb.h> |
27 | #include <netinet/in.h> |
28 | #include <arpa/inet.h> |
29 | #include <string.h> |
30 | #include <unistd.h> |
31 | #include <fcntl.h> |
32 | #include <errno.h> |
33 | |
cfa82921 |
34 | #include "Bot.H" |
35 | |
cb21075d |
36 | Socket::Socket() |
37 | : remotePort(0), remoteAddress(0), |
38 | localPort(0), localAddress(0) |
39 | { |
40 | fd = new s_fd; |
41 | fd->fd = socket(AF_INET, SOCK_STREAM, 0); |
42 | } |
43 | |
44 | Socket::Socket(int newfd, unsigned long newAddress, int newPort) |
45 | : remotePort(newPort), remoteAddress(newAddress), |
46 | localPort(0), localAddress(0) |
47 | { |
48 | fd = new s_fd; |
49 | fd->fd = newfd; |
50 | } |
51 | |
52 | Socket::Socket(const Socket &s) |
53 | : remotePort(s.remotePort), remoteAddress(s.remoteAddress), |
54 | localPort(s.localPort), localAddress(s.localAddress) |
55 | { |
56 | s.fd->n++; |
57 | fd = s.fd; |
58 | } |
59 | |
60 | Socket::~Socket() |
61 | { |
62 | if (--fd->n == 0) { |
63 | close(); |
64 | delete fd; |
65 | } |
66 | } |
67 | |
68 | int |
69 | Socket::getRemotePort() const |
70 | { |
71 | return remotePort; |
72 | } |
73 | |
74 | int |
75 | Socket::getLocalPort() const |
76 | { |
77 | return localPort; |
78 | } |
79 | |
80 | int |
81 | Socket::getFileDescriptor() const |
82 | { |
83 | return fd->fd; |
84 | } |
85 | |
86 | bool |
87 | Socket::isConnected() |
88 | { |
89 | return fd->fd != -1; |
90 | } |
91 | |
92 | bool |
93 | Socket::setRemoteHostname(String hostname) |
94 | { |
95 | struct hostent *host; |
96 | |
815a1816 |
97 | if ((host = gethostbyname(hostname.c_str ())) == 0) |
cb21075d |
98 | return false; |
99 | |
100 | memcpy(&remoteAddress, host->h_addr, host->h_length); |
101 | remoteAddress = ntohl(remoteAddress); |
102 | |
103 | return true; |
104 | } |
105 | |
106 | bool |
107 | Socket::setRemoteIP(unsigned long address) |
108 | { |
109 | remoteAddress = address; |
110 | return true; |
111 | } |
112 | |
113 | bool |
114 | Socket::setRemotePort(int p) |
115 | { |
116 | remotePort = p; |
117 | |
118 | return true; |
119 | } |
120 | |
121 | bool |
122 | Socket::setLocalHostname(String hostname) |
123 | { |
124 | struct hostent *host; |
125 | |
815a1816 |
126 | if ((host = gethostbyname(hostname.c_str ())) == 0) |
cb21075d |
127 | return false; |
128 | |
129 | memcpy(&localAddress, host->h_addr, host->h_length); |
130 | |
131 | struct sockaddr_in local_addr; |
132 | local_addr.sin_family = AF_INET; |
133 | local_addr.sin_port = htons(localPort); |
134 | local_addr.sin_addr.s_addr = htonl(localAddress); |
135 | if (bind(fd->fd, (struct sockaddr *)(&local_addr), sizeof(local_addr)) < 0) |
136 | return false; |
137 | |
138 | return true; |
139 | } |
140 | |
141 | bool |
142 | Socket::setLocalIP(unsigned long address) |
143 | { |
144 | localAddress = address; |
145 | |
146 | struct sockaddr_in local_addr; |
147 | local_addr.sin_family = AF_INET; |
148 | local_addr.sin_port = htons(localPort); |
149 | local_addr.sin_addr.s_addr = htonl(localAddress); |
150 | if (bind(fd->fd, (struct sockaddr *)(&local_addr), sizeof(local_addr)) < 0) |
151 | return false; |
152 | |
153 | return true; |
154 | } |
155 | |
156 | bool |
157 | Socket::setLocalPort(int p) |
158 | { |
159 | localPort = p; |
160 | |
161 | struct sockaddr_in local_addr; |
162 | local_addr.sin_family = AF_INET; |
163 | local_addr.sin_port = htons(localPort); |
164 | local_addr.sin_addr.s_addr = htonl(localAddress); |
165 | if (bind(fd->fd, (struct sockaddr *)(&local_addr), sizeof(local_addr)) < 0) |
166 | return false; |
167 | |
168 | return true; |
169 | } |
170 | |
171 | bool |
172 | Socket::setNonBlocking(bool nonblock) |
173 | { |
174 | long flags; |
175 | |
176 | if (fd->fd == -1) |
177 | return false; |
178 | |
179 | // We first get the file descriptor's flags |
180 | if (!fcntl(fd->fd, F_GETFL, &flags)) |
181 | return false; |
182 | |
183 | if (nonblock) |
184 | return fcntl(fd->fd, F_SETFL, flags | O_NONBLOCK); |
185 | else |
186 | return fcntl(fd->fd, F_SETFL, flags & ~O_NONBLOCK); |
187 | } |
188 | |
189 | bool |
190 | Socket::connect() |
191 | { |
192 | struct sockaddr_in addr; |
193 | memset(&addr, 0, sizeof(addr)); |
194 | addr.sin_family = AF_INET; |
195 | addr.sin_addr.s_addr = htonl(remoteAddress); |
196 | addr.sin_port = htons(remotePort); |
197 | if (::connect(fd->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) |
fd7440f1 |
198 | { |
4edefeb6 |
199 | // Bot::logLine (String("Socket Error: ") |
200 | // + strerror (errno) + String("\n")); |
fd7440f1 |
201 | return false; |
202 | } |
cb21075d |
203 | |
204 | return true; |
205 | } |
206 | |
207 | bool |
208 | Socket::listen(int backlog) |
209 | { |
210 | return ::listen(fd->fd, backlog) == 0; |
211 | } |
212 | |
213 | Socket |
214 | Socket::accept() |
215 | { |
216 | struct sockaddr_in addr; |
217 | socklen_t addrlen=sizeof(addr); |
218 | int newfd = ::accept(fd->fd, (sockaddr *)&addr, &addrlen); |
219 | if (newfd == -1) |
220 | return Socket(-1, 0, 0); |
221 | unsigned long newRemoteAddress = ntohl(addr.sin_addr.s_addr); |
222 | int newRemotePort = ntohs(addr.sin_port); |
223 | return Socket(newfd, newRemoteAddress, newRemotePort); |
224 | } |
225 | |
226 | void |
227 | Socket::close() |
228 | { |
229 | ::close(fd->fd); |
230 | fd->fd = -1; |
231 | } |
232 | |
233 | bool |
234 | Socket::write(String s, bool m) |
235 | { |
236 | if (fd->fd == -1) |
237 | return false; |
238 | |
239 | if (m) { |
815a1816 |
240 | if (::write(fd->fd, s.c_str (), s.length()) + |
cb21075d |
241 | ::write(fd->fd, "\n", 1) != s.length() + 1) |
242 | return false; |
243 | } |
244 | else |
815a1816 |
245 | if (::write(fd->fd, s.c_str (), s.length()) + |
cb21075d |
246 | ::write(fd->fd, "\r\n", 2) != s.length() + 2) |
247 | return false; |
248 | |
249 | return true; |
250 | } |
251 | |
252 | String |
253 | Socket::readLine() |
254 | { |
005c31fb |
255 | // fixme: this could probably be sped up (use some faster way of |
256 | // reading from the socket than reading a char at a time) |
257 | |
258 | // We allocate the buffer statically to keep from having to overhead |
259 | // of reallocating a new buffer every time we call this (which is |
260 | // every time anything happens on IRC so the overhead of |
261 | // re-allocating a buffer every time is potentially too large). |
262 | // |
263 | // Since it is static, and the length of lines will differ for each |
264 | // read, an embedded \0 is inserted after the last character of the |
265 | // line and then a copy of the the c_str is returned as a String so |
266 | // that only the line that was just read is returned. |
fd7440f1 |
267 | static std::string buf (512, ' '); |
fd7440f1 |
268 | |
cf8ea873 |
269 | std::string::size_type pos = 0; // pos in buffer |
005c31fb |
270 | int nb; // number of bytes read by ::read |
271 | char r; // temp var for storing output of read into |
272 | std::string::size_type length = buf.length (); |
273 | |
439869bf |
274 | do |
275 | { |
276 | nb = ::read(fd->fd, &r, 1); |
277 | switch (nb) { |
278 | case 0: |
279 | return String(""); |
280 | case -1: |
281 | if (errno != EINTR && errno != EAGAIN) |
282 | return String(""); |
283 | sleep(1); |
284 | } |
285 | |
286 | if (nb != -1) |
fd7440f1 |
287 | if (pos < length) |
288 | buf[pos++] = r; |
289 | else |
290 | { |
291 | buf.resize (length * 2); |
292 | length = buf.length (); |
293 | buf[pos++] = r; |
294 | } |
439869bf |
295 | } while (r != '\n'); |
cb21075d |
296 | |
005c31fb |
297 | if (pos > 1 && buf[pos-2] == '\r') |
7b564711 |
298 | { |
299 | buf[pos-2] = '\0'; |
300 | return String (buf.substr (0, pos - 2)); |
301 | } |
cb21075d |
302 | else |
7b564711 |
303 | { |
304 | buf[pos-1] = '\0'; |
305 | return String (buf.substr (0, pos - 1)); |
306 | } |
cb21075d |
307 | } |
308 | |
309 | String |
310 | Socket::readChar() |
311 | { |
312 | char r[2]; |
313 | int nb; |
314 | nb = ::read(fd->fd, &r, 1); |
315 | switch (nb) { |
316 | case 0: |
317 | return String(""); |
318 | case -1: |
319 | if(errno != EINTR && errno != EAGAIN) |
320 | return String(""); |
321 | sleep(1); |
322 | } |
323 | r[1] = '\0'; |
324 | return String(r); |
325 | } |
326 | |
327 | |
328 | // // Here we use a circular buffer to read from the socket |
329 | // String |
330 | // Socket::readLine() |
331 | // { |
332 | // // The result string |
333 | // char result[512]; |
334 | // // Position in the result string |
335 | // int pos = 0; |
336 | // // Number of chars read from the socket |
337 | // int nb; |
338 | |
339 | // // First, we read from the socket |
340 | // beginning: |
341 | // if (end <= begin) { |
342 | // nb = ::read(fd->fd, buf+end, begin - end); |
343 | // } else { |
344 | // nb = ::read(fd->fd, buf+end, 1024 - end); |
345 | // } |
346 | |
347 | // if (nb == -1 && errno != EINTR && errno != EAGAIN) |
348 | // return String(""); |
349 | |
350 | // end = (end + nb) % 1024; |
351 | |
352 | // // Fine, now we read our string from the buffer |
353 | // while (buf[begin] != '\r' && buf[begin] != '\n') { |
354 | // result[pos++] = buf[begin++]; |
355 | // begin %= 1024; |
356 | // if (begin == end) |
357 | // goto beginning; |
358 | // } |
359 | |
360 | // result[pos] = '\0'; |
361 | |
362 | // // Now we skip the final '\r' and '\n' |
363 | // if (buf[begin] == '\r') |
364 | // begin = (begin + 1) % 1024; |
365 | // if (buf[begin] == '\n') |
366 | // begin = (begin + 1) % 1024; |
367 | |
368 | // // And we return the result |
369 | // return String(result); |
370 | // } |
371 | |
372 | // bool |
373 | // Socket::hasData() |
374 | // { |
375 | // cout << "DEBUG hasData = " << (begin != end) << endl; |
376 | // return begin != end; |
377 | // } |