diff options
Diffstat (limited to 'linden/indra/llmessage/net.cpp')
-rw-r--r-- | linden/indra/llmessage/net.cpp | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/linden/indra/llmessage/net.cpp b/linden/indra/llmessage/net.cpp new file mode 100644 index 0000000..d21165e --- /dev/null +++ b/linden/indra/llmessage/net.cpp | |||
@@ -0,0 +1,539 @@ | |||
1 | /** | ||
2 | * @file net.cpp | ||
3 | * @brief Cross-platform routines for sending and receiving packets. | ||
4 | * | ||
5 | * Copyright (c) 2000-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | #include "linden_common.h" | ||
29 | |||
30 | #include "net.h" | ||
31 | |||
32 | // system library includes | ||
33 | #include <stdexcept> | ||
34 | #include <stdio.h> | ||
35 | |||
36 | #if LL_WINDOWS | ||
37 | #define WIN32_LEAN_AND_MEAN | ||
38 | #include <winsock2.h> | ||
39 | #include <windows.h> | ||
40 | #else | ||
41 | #include <sys/types.h> | ||
42 | #include <sys/socket.h> | ||
43 | #include <netinet/in.h> | ||
44 | #include <arpa/inet.h> | ||
45 | #include <fcntl.h> | ||
46 | #include <errno.h> | ||
47 | #endif | ||
48 | |||
49 | // linden library includes | ||
50 | #include "network.h" | ||
51 | #include "llerror.h" | ||
52 | #include "llhost.h" | ||
53 | #include "lltimer.h" | ||
54 | #include "indra_constants.h" | ||
55 | |||
56 | |||
57 | // Globals | ||
58 | #if LL_WINDOWS | ||
59 | |||
60 | SOCKADDR_IN stDstAddr; | ||
61 | SOCKADDR_IN stSrcAddr; | ||
62 | SOCKADDR_IN stLclAddr; | ||
63 | static WSADATA stWSAData; | ||
64 | |||
65 | #else | ||
66 | |||
67 | struct sockaddr_in stDstAddr; | ||
68 | struct sockaddr_in stSrcAddr; | ||
69 | struct sockaddr_in stLclAddr; | ||
70 | |||
71 | #if LL_DARWIN | ||
72 | #ifndef _SOCKLEN_T | ||
73 | #define _SOCKLEN_T | ||
74 | typedef int socklen_t; | ||
75 | #endif | ||
76 | #endif | ||
77 | |||
78 | #endif | ||
79 | |||
80 | |||
81 | const char* LOOPBACK_ADDRESS_STRING = "127.0.0.1"; | ||
82 | |||
83 | #if LL_DARWIN | ||
84 | // Mac OS X returns an error when trying to set these to 400000. Smaller values succeed. | ||
85 | const int SEND_BUFFER_SIZE = 200000; | ||
86 | const int RECEIVE_BUFFER_SIZE = 200000; | ||
87 | #else // LL_DARWIN | ||
88 | const int SEND_BUFFER_SIZE = 400000; | ||
89 | const int RECEIVE_BUFFER_SIZE = 400000; | ||
90 | #endif // LL_DARWIN | ||
91 | |||
92 | // universal functions (cross-platform) | ||
93 | |||
94 | LLHost get_sender() | ||
95 | { | ||
96 | return LLHost(stSrcAddr.sin_addr.s_addr, ntohs(stSrcAddr.sin_port)); | ||
97 | } | ||
98 | |||
99 | U32 get_sender_ip(void) | ||
100 | { | ||
101 | return stSrcAddr.sin_addr.s_addr; | ||
102 | } | ||
103 | |||
104 | U32 get_sender_port() | ||
105 | { | ||
106 | return ntohs(stSrcAddr.sin_port); | ||
107 | } | ||
108 | |||
109 | const char* u32_to_ip_string(U32 ip) | ||
110 | { | ||
111 | static char buffer[MAXADDRSTR]; /* Flawfinder: ignore */ | ||
112 | |||
113 | // Convert the IP address into a string | ||
114 | in_addr in; | ||
115 | in.s_addr = ip; | ||
116 | char* result = inet_ntoa(in); | ||
117 | |||
118 | // NULL indicates error in conversion | ||
119 | if (result != NULL) | ||
120 | { | ||
121 | strncpy( buffer, result, MAXADDRSTR ); /* Flawfinder: ignore */ | ||
122 | buffer[MAXADDRSTR-1] = '\0'; | ||
123 | return buffer; | ||
124 | } | ||
125 | else | ||
126 | { | ||
127 | return "(bad IP addr)"; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | |||
132 | // Returns ip_string if successful, NULL if not. Copies into ip_string | ||
133 | char *u32_to_ip_string(U32 ip, char *ip_string) | ||
134 | { | ||
135 | char *result; | ||
136 | in_addr in; | ||
137 | |||
138 | // Convert the IP address into a string | ||
139 | in.s_addr = ip; | ||
140 | result = inet_ntoa(in); | ||
141 | |||
142 | // NULL indicates error in conversion | ||
143 | if (result != NULL) | ||
144 | { | ||
145 | //the function signature needs to change to pass in the lengfth of first and last. | ||
146 | strcpy(ip_string, result); | ||
147 | return ip_string; | ||
148 | } | ||
149 | else | ||
150 | { | ||
151 | return NULL; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | |||
156 | // Wrapper for inet_addr() | ||
157 | U32 ip_string_to_u32(const char* ip_string) | ||
158 | { | ||
159 | return inet_addr(ip_string); | ||
160 | } | ||
161 | |||
162 | |||
163 | ////////////////////////////////////////////////////////////////////////////////////////// | ||
164 | // Windows Versions | ||
165 | ////////////////////////////////////////////////////////////////////////////////////////// | ||
166 | |||
167 | #if LL_WINDOWS | ||
168 | |||
169 | S32 start_net(S32& socket_out, int& nPort) | ||
170 | { | ||
171 | // Create socket, make non-blocking | ||
172 | // Init WinSock | ||
173 | int nRet; | ||
174 | int hSocket; | ||
175 | |||
176 | int snd_size = SEND_BUFFER_SIZE; | ||
177 | int rec_size = RECEIVE_BUFFER_SIZE; | ||
178 | int buff_size = 4; | ||
179 | |||
180 | // Initialize windows specific stuff | ||
181 | if(WSAStartup(0x0202, &stWSAData)) | ||
182 | { | ||
183 | S32 err = WSAGetLastError(); | ||
184 | WSACleanup(); | ||
185 | llwarns << "Windows Sockets initialization failed, err " << err << llendl; | ||
186 | return 1; | ||
187 | } | ||
188 | |||
189 | // Get a datagram socket | ||
190 | hSocket = (int)socket(AF_INET, SOCK_DGRAM, 0); | ||
191 | if (hSocket == INVALID_SOCKET) | ||
192 | { | ||
193 | S32 err = WSAGetLastError(); | ||
194 | WSACleanup(); | ||
195 | llwarns << "socket() failed, err " << err << llendl; | ||
196 | return 2; | ||
197 | } | ||
198 | |||
199 | // Name the socket (assign the local port number to receive on) | ||
200 | stLclAddr.sin_family = AF_INET; | ||
201 | stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY); | ||
202 | stLclAddr.sin_port = htons(nPort); | ||
203 | |||
204 | S32 attempt_port = nPort; | ||
205 | llinfos << "attempting to connect on port " << attempt_port << llendl; | ||
206 | nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr)); | ||
207 | |||
208 | if (nRet == SOCKET_ERROR) | ||
209 | { | ||
210 | // If we got an address in use error... | ||
211 | if (WSAGetLastError() == WSAEADDRINUSE) | ||
212 | { | ||
213 | // Try all ports from PORT_DISCOVERY_RANGE_MIN to PORT_DISCOVERY_RANGE_MAX | ||
214 | for(attempt_port = PORT_DISCOVERY_RANGE_MIN; | ||
215 | attempt_port <= PORT_DISCOVERY_RANGE_MAX; | ||
216 | attempt_port++) | ||
217 | { | ||
218 | stLclAddr.sin_port = htons(attempt_port); | ||
219 | llinfos << "trying port " << attempt_port << llendl; | ||
220 | nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr)); | ||
221 | |||
222 | if (!(nRet == SOCKET_ERROR && | ||
223 | WSAGetLastError() == WSAEADDRINUSE)) | ||
224 | { | ||
225 | break; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | if (nRet == SOCKET_ERROR) | ||
230 | { | ||
231 | llwarns << "startNet() : Couldn't find available network port." << llendl; | ||
232 | // Fail gracefully here in release | ||
233 | return 3; | ||
234 | } | ||
235 | } | ||
236 | else | ||
237 | // Some other socket error | ||
238 | { | ||
239 | llwarns << llformat("bind() port: %d failed, Err: %d\n", nPort, WSAGetLastError()) << llendl; | ||
240 | // Fail gracefully in release. | ||
241 | return 4; | ||
242 | } | ||
243 | } | ||
244 | llinfos << "connected on port " << attempt_port << llendl; | ||
245 | nPort = attempt_port; | ||
246 | |||
247 | // Set socket to be non-blocking | ||
248 | unsigned long argp = 1; | ||
249 | nRet = ioctlsocket (hSocket, FIONBIO, &argp); | ||
250 | if (nRet == SOCKET_ERROR) | ||
251 | { | ||
252 | printf("Failed to set socket non-blocking, Err: %d\n", | ||
253 | WSAGetLastError()); | ||
254 | } | ||
255 | |||
256 | // set a large receive buffer | ||
257 | nRet = setsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, buff_size); | ||
258 | if (nRet) | ||
259 | { | ||
260 | llinfos << "Can't set receive buffer size!" << llendl; | ||
261 | } | ||
262 | |||
263 | nRet = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, buff_size); | ||
264 | if (nRet) | ||
265 | { | ||
266 | llinfos << "Can't set send buffer size!" << llendl; | ||
267 | } | ||
268 | |||
269 | getsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, &buff_size); | ||
270 | getsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, &buff_size); | ||
271 | |||
272 | llinfos << "startNet - receive buffer size : " << rec_size << llendl; | ||
273 | llinfos << "startNet - send buffer size : " << snd_size << llendl; | ||
274 | |||
275 | // Setup a destination address | ||
276 | char achMCAddr[MAXADDRSTR] = " "; /* Flawfinder: ignore */ | ||
277 | stDstAddr.sin_family = AF_INET; | ||
278 | stDstAddr.sin_addr.s_addr = inet_addr(achMCAddr); | ||
279 | stDstAddr.sin_port = htons(nPort); | ||
280 | |||
281 | socket_out = hSocket; | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | void end_net() | ||
286 | { | ||
287 | WSACleanup(); | ||
288 | } | ||
289 | |||
290 | S32 receive_packet(int hSocket, char * receiveBuffer) | ||
291 | { | ||
292 | // Receives data asynchronously from the socket set by initNet(). | ||
293 | // Returns the number of bytes received into dataReceived, or zero | ||
294 | // if there is no data received. | ||
295 | int nRet; | ||
296 | int addr_size = sizeof(struct sockaddr_in); | ||
297 | |||
298 | nRet = recvfrom(hSocket, receiveBuffer, NET_BUFFER_SIZE, 0, (struct sockaddr*)&stSrcAddr, &addr_size); | ||
299 | if (nRet == SOCKET_ERROR ) | ||
300 | { | ||
301 | if (WSAEWOULDBLOCK == WSAGetLastError()) | ||
302 | return 0; | ||
303 | if (WSAECONNRESET == WSAGetLastError()) | ||
304 | return 0; | ||
305 | llinfos << "receivePacket() failed, Error: " << WSAGetLastError() << llendl; | ||
306 | } | ||
307 | |||
308 | return nRet; | ||
309 | } | ||
310 | |||
311 | // Returns TRUE on success. | ||
312 | BOOL send_packet(int hSocket, const char *sendBuffer, int size, U32 recipient, int nPort) | ||
313 | { | ||
314 | // Sends a packet to the address set in initNet | ||
315 | // | ||
316 | int nRet = 0; | ||
317 | U32 last_error = 0; | ||
318 | |||
319 | stDstAddr.sin_addr.s_addr = recipient; | ||
320 | stDstAddr.sin_port = htons(nPort); | ||
321 | do | ||
322 | { | ||
323 | nRet = sendto(hSocket, sendBuffer, size, 0, (struct sockaddr*)&stDstAddr, sizeof(stDstAddr)); | ||
324 | |||
325 | if (nRet == SOCKET_ERROR ) | ||
326 | { | ||
327 | last_error = WSAGetLastError(); | ||
328 | if (last_error != WSAEWOULDBLOCK) | ||
329 | { | ||
330 | // WSAECONNRESET - I think this is caused by an ICMP "connection refused" | ||
331 | // message being sent back from a Linux box... I'm not finding helpful | ||
332 | // documentation or web pages on this. The question is whether the packet | ||
333 | // actually got sent or not. Based on the structure of this code, I would | ||
334 | // assume it is. JNC 2002.01.18 | ||
335 | if (WSAECONNRESET == WSAGetLastError()) | ||
336 | { | ||
337 | return TRUE; | ||
338 | } | ||
339 | llinfos << "sendto() failed to " << u32_to_ip_string(recipient) << ":" << nPort | ||
340 | << ", Error " << last_error << llendl; | ||
341 | } | ||
342 | } | ||
343 | } while ( (nRet == SOCKET_ERROR) | ||
344 | &&(last_error == WSAEWOULDBLOCK)); | ||
345 | |||
346 | return (nRet != SOCKET_ERROR); | ||
347 | } | ||
348 | |||
349 | ////////////////////////////////////////////////////////////////////////////////////////// | ||
350 | // Linux Versions | ||
351 | ////////////////////////////////////////////////////////////////////////////////////////// | ||
352 | |||
353 | #else | ||
354 | |||
355 | // Create socket, make non-blocking | ||
356 | S32 start_net(S32& socket_out, int& nPort) | ||
357 | { | ||
358 | int hSocket, nRet; | ||
359 | int snd_size = SEND_BUFFER_SIZE; | ||
360 | int rec_size = RECEIVE_BUFFER_SIZE; | ||
361 | |||
362 | socklen_t buff_size = 4; | ||
363 | |||
364 | // Create socket | ||
365 | hSocket = socket(AF_INET, SOCK_DGRAM, 0); | ||
366 | if (hSocket < 0) | ||
367 | { | ||
368 | llwarns << "socket() failed" << llendl; | ||
369 | return 1; | ||
370 | } | ||
371 | |||
372 | // Don't bind() if we want the operating system to assign our ports for | ||
373 | // us. | ||
374 | if (NET_USE_OS_ASSIGNED_PORT == nPort) | ||
375 | { | ||
376 | // Do nothing; the operating system will do it for us. | ||
377 | } | ||
378 | else | ||
379 | { | ||
380 | // Name the socket (assign the local port number to receive on) | ||
381 | stLclAddr.sin_family = AF_INET; | ||
382 | stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY); | ||
383 | stLclAddr.sin_port = htons(nPort); | ||
384 | U32 attempt_port = nPort; | ||
385 | llinfos << "attempting to connect on port " << attempt_port << llendl; | ||
386 | |||
387 | nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr)); | ||
388 | if (nRet < 0) | ||
389 | { | ||
390 | // If we got an address in use error... | ||
391 | if (errno == EADDRINUSE) | ||
392 | { | ||
393 | // Try all ports from PORT_DISCOVERY_RANGE_MIN to PORT_DISCOVERY_RANGE_MAX | ||
394 | for(attempt_port = PORT_DISCOVERY_RANGE_MIN; | ||
395 | attempt_port <= PORT_DISCOVERY_RANGE_MAX; | ||
396 | attempt_port++) | ||
397 | { | ||
398 | stLclAddr.sin_port = htons(attempt_port); | ||
399 | llinfos << "trying port " << attempt_port << llendl; | ||
400 | nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr)); | ||
401 | if (!((nRet < 0) && (errno == EADDRINUSE))) | ||
402 | { | ||
403 | break; | ||
404 | } | ||
405 | } | ||
406 | if (nRet < 0) | ||
407 | { | ||
408 | llwarns << "startNet() : Couldn't find available network port." << llendl; | ||
409 | // Fail gracefully in release. | ||
410 | return 3; | ||
411 | } | ||
412 | } | ||
413 | // Some other socket error | ||
414 | else | ||
415 | { | ||
416 | llwarns << llformat ("bind() port: %d failed, Err: %s\n", nPort, strerror(errno)) << llendl; | ||
417 | // Fail gracefully in release. | ||
418 | return 4; | ||
419 | } | ||
420 | } | ||
421 | llinfos << "connected on port " << attempt_port << llendl; | ||
422 | nPort = attempt_port; | ||
423 | } | ||
424 | // Set socket to be non-blocking | ||
425 | fcntl(hSocket, F_SETFL, O_NONBLOCK); | ||
426 | // set a large receive buffer | ||
427 | nRet = setsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, buff_size); | ||
428 | if (nRet) | ||
429 | { | ||
430 | llinfos << "Can't set receive size!" << llendl; | ||
431 | } | ||
432 | nRet = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, buff_size); | ||
433 | if (nRet) | ||
434 | { | ||
435 | llinfos << "Can't set send size!" << llendl; | ||
436 | } | ||
437 | getsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, &buff_size); | ||
438 | getsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, &buff_size); | ||
439 | |||
440 | llinfos << "startNet - receive buffer size : " << rec_size << llendl; | ||
441 | llinfos << "startNet - send buffer size : " << snd_size << llendl; | ||
442 | |||
443 | // Setup a destination address | ||
444 | char achMCAddr[MAXADDRSTR] = "127.0.0.1"; /* Flawfinder: ignore */ | ||
445 | stDstAddr.sin_family = AF_INET; | ||
446 | stDstAddr.sin_addr.s_addr = inet_addr(achMCAddr); | ||
447 | stDstAddr.sin_port = htons(nPort); | ||
448 | |||
449 | socket_out = hSocket; | ||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | void end_net() | ||
454 | { | ||
455 | } | ||
456 | |||
457 | int receive_packet(int hSocket, char * receiveBuffer) | ||
458 | { | ||
459 | // Receives data asynchronously from the socket set by initNet(). | ||
460 | // Returns the number of bytes received into dataReceived, or zero | ||
461 | // if there is no data received. | ||
462 | // or -1 if an error occured! | ||
463 | int nRet; | ||
464 | socklen_t addr_size = sizeof(struct sockaddr_in); | ||
465 | |||
466 | nRet = recvfrom(hSocket, receiveBuffer, NET_BUFFER_SIZE, 0, (struct sockaddr*)&stSrcAddr, &addr_size); | ||
467 | |||
468 | if (nRet == -1) | ||
469 | { | ||
470 | // To maintain consistency with the Windows implementation, return a zero for size on error. | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | return nRet; | ||
475 | } | ||
476 | |||
477 | BOOL send_packet(int hSocket, const char * sendBuffer, int size, U32 recipient, int nPort) | ||
478 | { | ||
479 | int ret; | ||
480 | BOOL success; | ||
481 | BOOL resend; | ||
482 | S32 send_attempts = 0; | ||
483 | |||
484 | stDstAddr.sin_addr.s_addr = recipient; | ||
485 | stDstAddr.sin_port = htons(nPort); | ||
486 | |||
487 | do | ||
488 | { | ||
489 | ret = sendto(hSocket, sendBuffer, size, 0, (struct sockaddr*)&stDstAddr, sizeof(stDstAddr)); | ||
490 | send_attempts++; | ||
491 | |||
492 | if (ret >= 0) | ||
493 | { | ||
494 | // successful send | ||
495 | success = TRUE; | ||
496 | resend = FALSE; | ||
497 | } | ||
498 | else | ||
499 | { | ||
500 | // send failed, check to see if we should resend | ||
501 | success = FALSE; | ||
502 | |||
503 | if (errno == EAGAIN) | ||
504 | { | ||
505 | // say nothing, just repeat send | ||
506 | llinfos << "sendto() reported buffer full, resending (attempt " << send_attempts << ")" << llendl; | ||
507 | llinfos << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << llendl; | ||
508 | resend = TRUE; | ||
509 | } | ||
510 | else if (errno == ECONNREFUSED) | ||
511 | { | ||
512 | // response to ICMP connection refused message on earlier send | ||
513 | llinfos << "sendto() reported connection refused, resending (attempt " << send_attempts << ")" << llendl; | ||
514 | llinfos << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << llendl; | ||
515 | resend = TRUE; | ||
516 | } | ||
517 | else | ||
518 | { | ||
519 | // some other error | ||
520 | llinfos << "sendto() failed: " << errno << ", " << strerror(errno) << llendl; | ||
521 | llinfos << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << llendl; | ||
522 | resend = FALSE; | ||
523 | } | ||
524 | } | ||
525 | } | ||
526 | while ( resend && send_attempts < 3); | ||
527 | |||
528 | if (send_attempts >= 3) | ||
529 | { | ||
530 | llinfos << "sendPacket() bailed out of send!" << llendl; | ||
531 | return FALSE; | ||
532 | } | ||
533 | |||
534 | return success; | ||
535 | } | ||
536 | |||
537 | #endif | ||
538 | |||
539 | //EOF | ||