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