Line data Source code
1 : /*
2 : Mosh: the mobile shell
3 : Copyright 2012 Keith Winstein
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 3 of the License, or
8 : (at your option) 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, see <http://www.gnu.org/licenses/>.
17 :
18 : In addition, as a special exception, the copyright holders give
19 : permission to link the code of portions of this program with the
20 : OpenSSL library under certain conditions as described in each
21 : individual source file, and distribute linked combinations including
22 : the two.
23 :
24 : You must obey the GNU General Public License in all respects for all
25 : of the code used other than OpenSSL. If you modify file(s) with this
26 : exception, you may extend this exception to your version of the
27 : file(s), but you are not obligated to do so. If you do not wish to do
28 : so, delete this exception statement from your version. If you delete
29 : this exception statement from all source files in the program, then
30 : also delete it here.
31 : */
32 :
33 : #include "config.h"
34 :
35 : #include <sys/types.h>
36 : #include <sys/socket.h>
37 : #ifdef HAVE_SYS_UIO_H
38 : #include <sys/uio.h>
39 : #endif
40 : #include <netdb.h>
41 : #include <netinet/in.h>
42 : #include <assert.h>
43 : #include <errno.h>
44 : #include <string.h>
45 : #include <unistd.h>
46 :
47 : #include "dos_assert.h"
48 : #include "fatal_assert.h"
49 : #include "byteorder.h"
50 : #include "network.h"
51 : #include "crypto.h"
52 :
53 : #include "timestamp.h"
54 :
55 : #ifndef MSG_DONTWAIT
56 : #define MSG_DONTWAIT MSG_NONBLOCK
57 : #endif
58 :
59 : #ifndef AI_NUMERICSERV
60 : #define AI_NUMERICSERV 0
61 : #endif
62 :
63 : using namespace Network;
64 : using namespace Crypto;
65 :
66 : const uint64_t DIRECTION_MASK = uint64_t(1) << 63;
67 : const uint64_t SEQUENCE_MASK = uint64_t(-1) ^ DIRECTION_MASK;
68 :
69 : /* Read in packet */
70 5285 : Packet::Packet( const Message & message )
71 5285 : : seq( message.nonce.val() & SEQUENCE_MASK ),
72 5285 : direction( (message.nonce.val() & DIRECTION_MASK) ? TO_CLIENT : TO_SERVER ),
73 5285 : timestamp( -1 ),
74 5285 : timestamp_reply( -1 ),
75 5285 : payload()
76 : {
77 5285 : dos_assert( message.text.size() >= 2 * sizeof( uint16_t ) );
78 :
79 5285 : const uint16_t *data = (uint16_t *)message.text.data();
80 5285 : timestamp = be16toh( data[ 0 ] );
81 5285 : timestamp_reply = be16toh( data[ 1 ] );
82 :
83 5285 : payload = string( message.text.begin() + 2 * sizeof( uint16_t ), message.text.end() );
84 5285 : }
85 :
86 : /* Output from packet */
87 8005333 : Message Packet::toMessage( void )
88 : {
89 8005333 : uint64_t direction_seq = (uint64_t( direction == TO_CLIENT ) << 63) | (seq & SEQUENCE_MASK);
90 :
91 8005333 : uint16_t ts_net[ 2 ] = { static_cast<uint16_t>( htobe16( timestamp ) ),
92 8005333 : static_cast<uint16_t>( htobe16( timestamp_reply ) ) };
93 :
94 8005333 : string timestamps = string( (char *)ts_net, 2 * sizeof( uint16_t ) );
95 :
96 16015999 : return Message( Nonce( direction_seq ), timestamps + payload );
97 8005333 : }
98 :
99 5333 : Packet Connection::new_packet( const string &s_payload )
100 : {
101 5333 : uint16_t outgoing_timestamp_reply = -1;
102 :
103 10666 : uint64_t now = timestamp();
104 :
105 5333 : if ( now - saved_timestamp_received_at < 1000 ) { /* we have a recent received timestamp */
106 : /* send "corrected" timestamp advanced by how long we held it */
107 2839 : outgoing_timestamp_reply = saved_timestamp + (now - saved_timestamp_received_at);
108 2839 : saved_timestamp = -1;
109 2839 : saved_timestamp_received_at = 0;
110 : }
111 :
112 15999 : Packet p( direction, timestamp16(), outgoing_timestamp_reply, s_payload );
113 :
114 5333 : return p;
115 : }
116 :
117 0 : void Connection::hop_port( void )
118 : {
119 0 : assert( !server );
120 :
121 0 : setup();
122 0 : assert( remote_addr_len != 0 );
123 0 : socks.push_back( Socket( remote_addr.sa.sa_family ) );
124 :
125 0 : prune_sockets();
126 0 : }
127 :
128 5285 : void Connection::prune_sockets( void )
129 : {
130 : /* don't keep old sockets if the new socket has been working for long enough */
131 5285 : if ( socks.size() > 1 ) {
132 0 : if ( timestamp() - last_port_choice > MAX_OLD_SOCKET_AGE ) {
133 0 : int num_to_kill = socks.size() - 1;
134 0 : for ( int i = 0; i < num_to_kill; i++ ) {
135 0 : socks.pop_front();
136 : }
137 : }
138 : } else {
139 : return;
140 : }
141 :
142 : /* make sure we don't have too many receive sockets open */
143 0 : if ( socks.size() > MAX_PORTS_OPEN ) {
144 0 : int num_to_kill = socks.size() - MAX_PORTS_OPEN;
145 0 : for ( int i = 0; i < num_to_kill; i++ ) {
146 0 : socks.pop_front();
147 : }
148 : }
149 : }
150 :
151 900 : Connection::Socket::Socket( int family )
152 900 : : _fd( socket( family, SOCK_DGRAM, 0 ) )
153 : {
154 900 : if ( _fd < 0 ) {
155 0 : throw NetworkException( "socket", errno );
156 : }
157 :
158 : /* Disable path MTU discovery */
159 : #ifdef HAVE_IP_MTU_DISCOVER
160 900 : int flag = IP_PMTUDISC_DONT;
161 900 : if ( setsockopt( _fd, IPPROTO_IP, IP_MTU_DISCOVER, &flag, sizeof flag ) < 0 ) {
162 0 : throw NetworkException( "setsockopt", errno );
163 : }
164 : #endif
165 :
166 : // int dscp = 0x92; /* OS X does not have IPTOS_DSCP_AF42 constant */
167 900 : int dscp = 0x02; /* ECN-capable transport only */
168 900 : if ( setsockopt( _fd, IPPROTO_IP, IP_TOS, &dscp, sizeof dscp ) < 0 ) {
169 : // perror( "setsockopt( IP_TOS )" );
170 : }
171 :
172 : /* request explicit congestion notification on received datagrams */
173 : #ifdef HAVE_IP_RECVTOS
174 900 : int tosflag = true;
175 900 : if ( setsockopt( _fd, IPPROTO_IP, IP_RECVTOS, &tosflag, sizeof tosflag ) < 0
176 900 : && family == IPPROTO_IP ) { /* FreeBSD disallows this option on IPv6 sockets. */
177 0 : perror( "setsockopt( IP_RECVTOS )" );
178 : }
179 : #endif
180 900 : }
181 :
182 900 : void Connection::setup( void )
183 : {
184 0 : last_port_choice = timestamp();
185 0 : }
186 :
187 33132 : const std::vector< int > Connection::fds( void ) const
188 : {
189 33132 : std::vector< int > ret;
190 :
191 33132 : for ( std::deque< Socket >::const_iterator it = socks.begin();
192 66264 : it != socks.end();
193 : it++ ) {
194 33132 : ret.push_back( it->fd() );
195 : }
196 :
197 33132 : return ret;
198 0 : }
199 :
200 900 : void Connection::set_MTU( int family )
201 : {
202 900 : switch ( family ) {
203 900 : case AF_INET:
204 900 : MTU = DEFAULT_IPV4_MTU - IPV4_HEADER_LEN;
205 900 : break;
206 0 : case AF_INET6:
207 0 : MTU = DEFAULT_IPV6_MTU - IPV6_HEADER_LEN;
208 0 : break;
209 0 : default:
210 0 : throw NetworkException( "Unknown address family", 0 );
211 : }
212 900 : }
213 :
214 : class AddrInfo {
215 : public:
216 : struct addrinfo *res;
217 900 : AddrInfo( const char *node, const char *service,
218 900 : const struct addrinfo *hints ) :
219 900 : res( NULL ) {
220 900 : int errcode = getaddrinfo( node, service, hints, &res );
221 900 : if ( errcode != 0 ) {
222 0 : throw NetworkException( std::string( "Bad IP address (" ) + (node != NULL ? node : "(null)") + "): " + gai_strerror( errcode ), 0 );
223 : }
224 900 : }
225 448 : ~AddrInfo() { freeaddrinfo(res); }
226 : private:
227 : AddrInfo(const AddrInfo &);
228 : AddrInfo &operator=(const AddrInfo &);
229 : };
230 :
231 452 : Connection::Connection( const char *desired_ip, const char *desired_port ) /* server */
232 452 : : socks(),
233 452 : has_remote_addr( false ),
234 452 : remote_addr(),
235 452 : remote_addr_len( 0 ),
236 452 : server( true ),
237 452 : MTU( DEFAULT_SEND_MTU ),
238 452 : key(),
239 452 : session( key ),
240 452 : direction( TO_CLIENT ),
241 452 : saved_timestamp( -1 ),
242 452 : saved_timestamp_received_at( 0 ),
243 452 : expected_receiver_seq( 0 ),
244 452 : last_heard( -1 ),
245 452 : last_port_choice( -1 ),
246 452 : last_roundtrip_success( -1 ),
247 452 : RTT_hit( false ),
248 452 : SRTT( 1000 ),
249 452 : RTTVAR( 500 ),
250 452 : send_error()
251 : {
252 904 : setup();
253 :
254 : /* The mosh wrapper always gives an IP request, in order
255 : to deal with multihomed servers. The port is optional. */
256 :
257 : /* If an IP request is given, we try to bind to that IP, but we also
258 : try INADDR_ANY. If a port request is given, we bind only to that port. */
259 :
260 : /* convert port numbers */
261 452 : int desired_port_low = -1;
262 452 : int desired_port_high = -1;
263 :
264 452 : if ( desired_port && !parse_portrange( desired_port, desired_port_low, desired_port_high ) ) {
265 0 : throw NetworkException("Invalid port range", 0);
266 : }
267 :
268 : /* try to bind to desired IP first */
269 452 : if ( desired_ip ) {
270 452 : try {
271 452 : if ( try_bind( desired_ip, desired_port_low, desired_port_high ) ) { return; }
272 0 : } catch ( const NetworkException &e ) {
273 0 : fprintf( stderr, "Error binding to IP %s: %s\n",
274 : desired_ip,
275 0 : e.what() );
276 0 : }
277 : }
278 :
279 : /* now try any local interface */
280 0 : try {
281 0 : if ( try_bind( NULL, desired_port_low, desired_port_high ) ) { return; }
282 0 : } catch ( const NetworkException &e ) {
283 0 : fprintf( stderr, "Error binding to any interface: %s\n",
284 0 : e.what() );
285 0 : throw; /* this time it's fatal */
286 0 : }
287 :
288 0 : throw NetworkException( "Could not bind", errno );
289 0 : }
290 :
291 452 : bool Connection::try_bind( const char *addr, int port_low, int port_high )
292 : {
293 452 : struct addrinfo hints;
294 452 : memset( &hints, 0, sizeof( hints ) );
295 452 : hints.ai_family = AF_UNSPEC;
296 452 : hints.ai_socktype = SOCK_DGRAM;
297 452 : hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV;
298 452 : AddrInfo ai( addr, "0", &hints );
299 :
300 452 : Addr local_addr;
301 452 : socklen_t local_addr_len = ai.res->ai_addrlen;
302 452 : memcpy( &local_addr.sa, ai.res->ai_addr, local_addr_len );
303 :
304 452 : int search_low = PORT_RANGE_LOW, search_high = PORT_RANGE_HIGH;
305 :
306 452 : if ( port_low != -1 ) { /* low port preference */
307 0 : search_low = port_low;
308 : }
309 452 : if ( port_high != -1 ) { /* high port preference */
310 0 : search_high = port_high;
311 : }
312 :
313 904 : socks.push_back( Socket( local_addr.sa.sa_family ) );
314 1874 : for ( int i = search_low; i <= search_high; i++ ) {
315 1874 : switch (local_addr.sa.sa_family) {
316 1874 : case AF_INET:
317 1874 : local_addr.sin.sin_port = htons( i );
318 1874 : break;
319 0 : case AF_INET6:
320 0 : local_addr.sin6.sin6_port = htons( i );
321 0 : break;
322 0 : default:
323 0 : throw NetworkException( "Unknown address family", 0 );
324 : }
325 :
326 1874 : if ( local_addr.sa.sa_family == AF_INET6
327 0 : && memcmp(&local_addr.sin6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 ) {
328 0 : const int off = 0;
329 0 : if ( setsockopt( sock(), IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off) ) ) {
330 0 : perror( "setsockopt( IPV6_V6ONLY, off )" );
331 : }
332 : }
333 :
334 1874 : if ( ::bind( sock(), &local_addr.sa, local_addr_len ) == 0 ) {
335 452 : set_MTU( local_addr.sa.sa_family );
336 452 : return true;
337 : } // else fallthrough to below code, on last iteration.
338 : }
339 0 : int saved_errno = errno;
340 0 : socks.pop_back();
341 0 : char host[ NI_MAXHOST ], serv[ NI_MAXSERV ];
342 0 : int errcode = getnameinfo( &local_addr.sa, local_addr_len,
343 : host, sizeof( host ), serv, sizeof( serv ),
344 : NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV );
345 0 : if ( errcode != 0 ) {
346 0 : throw NetworkException( std::string( "bind: getnameinfo: " ) + gai_strerror( errcode ), 0 );
347 : }
348 0 : fprintf( stderr, "Failed binding to %s:%s\n",
349 : host, serv );
350 0 : throw NetworkException( "bind", saved_errno );
351 452 : }
352 :
353 448 : Connection::Connection( const char *key_str, const char *ip, const char *port ) /* client */
354 448 : : socks(),
355 448 : has_remote_addr( false ),
356 448 : remote_addr(),
357 448 : remote_addr_len( 0 ),
358 448 : server( false ),
359 448 : MTU( DEFAULT_SEND_MTU ),
360 448 : key( key_str ),
361 448 : session( key ),
362 448 : direction( TO_SERVER ),
363 448 : saved_timestamp( -1 ),
364 448 : saved_timestamp_received_at( 0 ),
365 448 : expected_receiver_seq( 0 ),
366 448 : last_heard( -1 ),
367 448 : last_port_choice( -1 ),
368 448 : last_roundtrip_success( -1 ),
369 448 : RTT_hit( false ),
370 448 : SRTT( 1000 ),
371 448 : RTTVAR( 500 ),
372 448 : send_error()
373 : {
374 896 : setup();
375 :
376 : /* associate socket with remote host and port */
377 448 : struct addrinfo hints;
378 448 : memset( &hints, 0, sizeof( hints ) );
379 448 : hints.ai_family = AF_UNSPEC;
380 448 : hints.ai_socktype = SOCK_DGRAM;
381 448 : hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
382 448 : AddrInfo ai( ip, port, &hints );
383 448 : fatal_assert( static_cast<size_t>( ai.res->ai_addrlen ) <= sizeof( remote_addr ) );
384 448 : remote_addr_len = ai.res->ai_addrlen;
385 448 : memcpy( &remote_addr.sa, ai.res->ai_addr, remote_addr_len );
386 :
387 448 : has_remote_addr = true;
388 :
389 896 : socks.push_back( Socket( remote_addr.sa.sa_family ) );
390 :
391 448 : set_MTU( remote_addr.sa.sa_family );
392 448 : }
393 :
394 5333 : void Connection::send( const string & s )
395 : {
396 5333 : if ( !has_remote_addr ) {
397 0 : return;
398 : }
399 :
400 5333 : Packet px = new_packet( s );
401 :
402 5333 : string p = session.encrypt( px.toMessage() );
403 :
404 5333 : ssize_t bytes_sent = sendto( sock(), p.data(), p.size(), MSG_DONTWAIT,
405 5333 : &remote_addr.sa, remote_addr_len );
406 :
407 5333 : if ( bytes_sent != static_cast<ssize_t>( p.size() ) ) {
408 : /* Make sendto() failure available to the frontend. */
409 0 : send_error = "sendto: ";
410 0 : send_error += strerror( errno );
411 :
412 0 : if ( errno == EMSGSIZE ) {
413 0 : MTU = DEFAULT_SEND_MTU; /* payload MTU of last resort */
414 : }
415 : }
416 :
417 10666 : uint64_t now = timestamp();
418 5333 : if ( server ) {
419 3389 : if ( now - last_heard > SERVER_ASSOCIATION_TIMEOUT ) {
420 0 : has_remote_addr = false;
421 5333 : fprintf( stderr, "Server now detached from client.\n" );
422 : }
423 : } else { /* client */
424 1944 : if ( ( now - last_port_choice > PORT_HOP_INTERVAL )
425 284 : && ( now - last_roundtrip_success > PORT_HOP_INTERVAL ) ) {
426 0 : hop_port();
427 : }
428 : }
429 10666 : }
430 :
431 5285 : string Connection::recv( void )
432 : {
433 5285 : assert( !socks.empty() );
434 5285 : for ( std::deque< Socket >::const_iterator it = socks.begin();
435 5285 : it != socks.end();
436 : it++ ) {
437 5285 : string payload;
438 5285 : try {
439 5285 : payload = recv_one( it->fd());
440 0 : } catch ( NetworkException & e ) {
441 0 : if ( (e.the_errno == EAGAIN)
442 : || (e.the_errno == EWOULDBLOCK) ) {
443 0 : continue;
444 : } else {
445 0 : throw;
446 : }
447 0 : }
448 :
449 : /* succeeded */
450 5285 : prune_sockets();
451 10570 : return payload;
452 5285 : }
453 0 : throw NetworkException( "No packet received" );
454 : }
455 :
456 5285 : string Connection::recv_one( int sock_to_recv )
457 : {
458 : /* receive source address, ECN, and payload in msghdr structure */
459 5285 : Addr packet_remote_addr;
460 5285 : struct msghdr header;
461 5285 : struct iovec msg_iovec;
462 :
463 5285 : char msg_payload[ Session::RECEIVE_MTU ];
464 5285 : char msg_control[ Session::RECEIVE_MTU ];
465 :
466 : /* receive source address */
467 5285 : header.msg_name = &packet_remote_addr;
468 5285 : header.msg_namelen = sizeof packet_remote_addr;
469 :
470 : /* receive payload */
471 5285 : msg_iovec.iov_base = msg_payload;
472 5285 : msg_iovec.iov_len = sizeof msg_payload;
473 5285 : header.msg_iov = &msg_iovec;
474 5285 : header.msg_iovlen = 1;
475 :
476 : /* receive explicit congestion notification */
477 5285 : header.msg_control = msg_control;
478 5285 : header.msg_controllen = sizeof msg_control;
479 :
480 : /* receive flags */
481 5285 : header.msg_flags = 0;
482 :
483 5285 : ssize_t received_len = recvmsg( sock_to_recv, &header, MSG_DONTWAIT );
484 :
485 5285 : if ( received_len < 0 ) {
486 0 : throw NetworkException( "recvmsg", errno );
487 : }
488 :
489 5285 : if ( header.msg_flags & MSG_TRUNC ) {
490 0 : throw NetworkException( "Received oversize datagram", errno );
491 : }
492 :
493 : /* receive ECN */
494 5285 : bool congestion_experienced = false;
495 :
496 5285 : struct cmsghdr *ecn_hdr = CMSG_FIRSTHDR( &header );
497 5285 : if ( ecn_hdr
498 5285 : && ecn_hdr->cmsg_level == IPPROTO_IP
499 5285 : && ( ecn_hdr->cmsg_type == IP_TOS
500 : #ifdef IP_RECVTOS
501 5285 : || ecn_hdr->cmsg_type == IP_RECVTOS
502 : #endif
503 : ) ) {
504 : /* got one */
505 5285 : uint8_t *ecn_octet_p = (uint8_t *)CMSG_DATA( ecn_hdr );
506 5285 : assert( ecn_octet_p );
507 :
508 5285 : congestion_experienced = (*ecn_octet_p & 0x03) == 0x03;
509 : }
510 :
511 5285 : Packet p( session.decrypt( msg_payload, received_len ) );
512 :
513 8618 : dos_assert( p.direction == (server ? TO_SERVER : TO_CLIENT) ); /* prevent malicious playback to sender */
514 :
515 5285 : if ( p.seq < expected_receiver_seq ) { /* don't use (but do return) out-of-order packets for timestamp or targeting */
516 0 : return p.payload;
517 : }
518 5285 : expected_receiver_seq = p.seq + 1; /* this is security-sensitive because a replay attack could otherwise
519 : screw up the timestamp and targeting */
520 :
521 5285 : if ( p.timestamp != uint16_t(-1) ) {
522 5285 : saved_timestamp = p.timestamp;
523 10570 : saved_timestamp_received_at = timestamp();
524 :
525 5285 : if ( congestion_experienced ) {
526 : /* signal counterparty to slow down */
527 : /* this will gradually slow the counterparty down to the minimum frame rate */
528 0 : saved_timestamp -= CONGESTION_TIMESTAMP_PENALTY;
529 0 : if ( server ) {
530 0 : fprintf( stderr, "Received explicit congestion notification.\n" );
531 : }
532 : }
533 : }
534 :
535 5285 : if ( p.timestamp_reply != uint16_t(-1) ) {
536 5678 : uint16_t now = timestamp16();
537 5678 : double R = timestamp_diff( now, p.timestamp_reply );
538 :
539 2839 : if ( R < 5000 ) { /* ignore large values, e.g. server was Ctrl-Zed */
540 2839 : if ( !RTT_hit ) { /* first measurement */
541 900 : SRTT = R;
542 900 : RTTVAR = R / 2;
543 900 : RTT_hit = true;
544 : } else {
545 1939 : const double alpha = 1.0 / 8.0;
546 1939 : const double beta = 1.0 / 4.0;
547 :
548 1939 : RTTVAR = (1 - beta) * RTTVAR + ( beta * fabs( SRTT - R ) );
549 1939 : SRTT = (1 - alpha) * SRTT + ( alpha * R );
550 : }
551 : }
552 : }
553 :
554 : /* auto-adjust to remote host */
555 5285 : has_remote_addr = true;
556 10570 : last_heard = timestamp();
557 :
558 5285 : if ( server && /* only client can roam */
559 1952 : ( remote_addr_len != header.msg_namelen ||
560 1500 : memcmp( &remote_addr, &packet_remote_addr, remote_addr_len ) != 0 ) ) {
561 452 : remote_addr = packet_remote_addr;
562 452 : remote_addr_len = header.msg_namelen;
563 452 : char host[ NI_MAXHOST ], serv[ NI_MAXSERV ];
564 452 : int errcode = getnameinfo( &remote_addr.sa, remote_addr_len,
565 : host, sizeof( host ), serv, sizeof( serv ),
566 : NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV );
567 452 : if ( errcode != 0 ) {
568 0 : throw NetworkException( std::string( "recv_one: getnameinfo: " ) + gai_strerror( errcode ), 0 );
569 : }
570 904 : fprintf( stderr, "Server now attached to client at %s:%s\n",
571 : host, serv );
572 : }
573 5285 : return p.payload;
574 5285 : }
575 :
576 452 : std::string Connection::port( void ) const
577 : {
578 452 : Addr local_addr;
579 452 : socklen_t addrlen = sizeof( local_addr );
580 :
581 452 : if ( getsockname( sock(), &local_addr.sa, &addrlen ) < 0 ) {
582 0 : throw NetworkException( "getsockname", errno );
583 : }
584 :
585 452 : char serv[ NI_MAXSERV ];
586 452 : int errcode = getnameinfo( &local_addr.sa, addrlen,
587 : NULL, 0, serv, sizeof( serv ),
588 : NI_DGRAM | NI_NUMERICSERV );
589 452 : if ( errcode != 0 ) {
590 0 : throw NetworkException( std::string( "port: getnameinfo: " ) + gai_strerror( errcode ), 0 );
591 : }
592 :
593 452 : return std::string( serv );
594 : }
595 :
596 335721 : uint64_t Network::timestamp( void )
597 : {
598 327549 : return frozen_timestamp();
599 : }
600 :
601 8172 : uint16_t Network::timestamp16( void )
602 : {
603 8172 : uint16_t ts = timestamp() % 65536;
604 8172 : if ( ts == uint16_t(-1) ) {
605 0 : ts++;
606 : }
607 5333 : return ts;
608 : }
609 :
610 2839 : uint16_t Network::timestamp_diff( uint16_t tsnew, uint16_t tsold )
611 : {
612 2839 : int diff = tsnew - tsold;
613 2839 : if ( diff < 0 ) {
614 1 : diff += 65536;
615 : }
616 :
617 2839 : assert( diff >= 0 );
618 2839 : assert( diff <= 65535 );
619 :
620 2839 : return diff;
621 : }
622 :
623 127096 : uint64_t Connection::timeout( void ) const
624 : {
625 127096 : uint64_t RTO = lrint( ceil( SRTT + 4 * RTTVAR ) );
626 127096 : if ( RTO < MIN_RTO ) {
627 : RTO = MIN_RTO;
628 : } else if ( RTO > MAX_RTO ) {
629 : RTO = MAX_RTO;
630 : }
631 127096 : return RTO;
632 : }
633 :
634 2252 : Connection::Socket::~Socket()
635 : {
636 2252 : fatal_assert ( close( _fd ) == 0 );
637 2252 : }
638 :
639 900 : Connection::Socket::Socket( const Socket & other )
640 900 : : _fd( dup( other._fd ) )
641 : {
642 900 : if ( _fd < 0 ) {
643 0 : throw NetworkException( "socket", errno );
644 : }
645 900 : }
646 :
647 0 : Connection::Socket & Connection::Socket::operator=( const Socket & other )
648 : {
649 0 : if ( dup2( other._fd, _fd ) < 0 ) {
650 0 : throw NetworkException( "socket", errno );
651 : }
652 :
653 0 : return *this;
654 : }
655 :
656 0 : bool Connection::parse_portrange( const char * desired_port, int & desired_port_low, int & desired_port_high )
657 : {
658 : /* parse "port" or "portlow:porthigh" */
659 0 : desired_port_low = desired_port_high = 0;
660 0 : char *end;
661 0 : long value;
662 :
663 : /* parse first (only?) port */
664 0 : errno = 0;
665 0 : value = strtol( desired_port, &end, 10 );
666 0 : if ( (errno != 0) || (*end != '\0' && *end != ':') ) {
667 0 : fprintf( stderr, "Invalid (low) port number (%s)\n", desired_port );
668 0 : return false;
669 : }
670 0 : if ( (value < 0) || (value > 65535) ) {
671 0 : fprintf( stderr, "(Low) port number %ld outside valid range [0..65535]\n", value );
672 0 : return false;
673 : }
674 :
675 0 : desired_port_low = (int)value;
676 0 : if (*end == '\0') { /* not a port range */
677 0 : desired_port_high = desired_port_low;
678 0 : return true;
679 : }
680 : /* port range; parse high port */
681 0 : const char * cp = end + 1;
682 0 : errno = 0;
683 0 : value = strtol( cp, &end, 10 );
684 0 : if ( (errno != 0) || (*end != '\0') ) {
685 0 : fprintf( stderr, "Invalid high port number (%s)\n", cp );
686 0 : return false;
687 : }
688 0 : if ( (value < 0) || (value > 65535) ) {
689 0 : fprintf( stderr, "High port number %ld outside valid range [0..65535]\n", value );
690 0 : return false;
691 : }
692 :
693 0 : desired_port_high = (int)value;
694 0 : if ( desired_port_low > desired_port_high ) {
695 0 : fprintf( stderr, "Low port %d greater than high port %d\n", desired_port_low, desired_port_high );
696 0 : return false;
697 : }
698 :
699 0 : if ( desired_port_low == 0 ) {
700 0 : fprintf( stderr, "Low port 0 incompatible with port ranges\n" );
701 0 : return false;
702 : }
703 :
704 :
705 : return true;
706 : }
|