1/* @(#) implementation of network operations for udpxy
2 *
3 * Copyright 2008-2011 Pavel V. Cherenkov (pcherenkov@gmail.com)
4 *
5 *  This file is part of udpxy.
6 *
7 *  udpxy is free software: you can redistribute it and/or modify
8 *  it under the terms of the GNU General Public License as published by
9 *  the Free Software Foundation, either version 3 of the License, or
10 *  (at your option) any later version.
11 *
12 *  udpxy is distributed in the hope that it will be useful,
13 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 *  GNU General Public License for more details.
16 *
17 *  You should have received a copy of the GNU General Public License
18 *  along with udpxy.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <sys/types.h>
22#include <sys/socket.h>
23#include <arpa/inet.h>
24#include <netinet/in.h>
25
26#include <assert.h>
27#include <string.h>
28#include <unistd.h>
29#include <time.h>
30#include <errno.h>
31#include <stdio.h>
32#include <fcntl.h>
33
34#include "udpxy.h"
35#include "netop.h"
36#include "util.h"
37#include "mtrace.h"
38#include "osdef.h"
39
40
41extern FILE* g_flog;    /* application log */
42
43
44/* set up (server) listening sockfd
45 */
46int
47setup_listener( const char* ipaddr, int port, int* sockfd, int bklog )
48{
49#define LOWMARK 10 /* do not accept input of less than X octets */
50    int rc, lsock, wmark = LOWMARK;
51    struct sockaddr_in servaddr;
52    const int ON = 1;
53
54    extern const char IPv4_ALL[];
55
56    assert( (port > 0) && sockfd && ipaddr );
57    (void)IPv4_ALL;
58    TRACE( (void)tmfprintf( g_flog, "Setting up listener for [%s:%d]\n",
59                ipaddr[0] ? ipaddr : IPv4_ALL, port) );
60
61    rc = ERR_INTERNAL;
62    do {
63        lsock = socket( AF_INET, SOCK_STREAM, 0 );
64        if( -1 == lsock ) break;
65
66        (void) memset( &servaddr, 0, sizeof(servaddr) );
67        servaddr.sin_family = AF_INET;
68        servaddr.sin_port = htons( (short)port );
69
70        if( '\0' != ipaddr[0] ) {
71            if( 1 != inet_aton(ipaddr, &servaddr.sin_addr) ) {
72                TRACE( (void)tmfprintf( g_flog, "Invalid server IP: [%s]\n",
73                        ipaddr) );
74
75                rc = ERR_PARAM;
76                break;
77            }
78        }
79        else {
80            servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
81        }
82
83        rc = setsockopt( lsock, SOL_SOCKET, SO_REUSEADDR,
84                         &ON, sizeof(ON) );
85        if( 0 != rc ) {
86            mperror(g_flog, errno, "%s: setsockopt SO_REUSEADDR",
87                    __func__);
88            break;
89        }
90
91        #define NONBLOCK 1
92        rc = set_nblock (lsock, NONBLOCK);
93        if (0 != rc) break;
94
95        TRACE( (void)tmfprintf (g_flog, "Setting low watermark for "
96            "server socket [%d] to [%d]\n", lsock, wmark) );
97        rc = setsockopt (lsock, SOL_SOCKET, SO_RCVLOWAT,
98                (char*)&wmark, sizeof(wmark));
99        if (rc) {
100            mperror (g_flog, errno, "%s: setsockopt SO_RCVLOWAT",
101                __func__);
102            break;
103        }
104
105        rc = bind( lsock, (struct sockaddr*)&servaddr, sizeof(servaddr) );
106        if( 0 != rc ) break;
107
108        rc = listen (lsock, (bklog > 0 ? bklog : 1));
109        if( 0 != rc ) break;
110
111        rc = 0;
112    } while(0);
113
114    if( 0 != rc ) {
115        if(errno)
116            mperror(g_flog, errno, "%s: socket/bind/listen error",
117                    __func__);
118        if( lsock ) {
119            (void) close( lsock );
120        }
121    }
122    else {
123        *sockfd = lsock;
124        TRACE( (void)tmfprintf( g_flog, "Created server socket=[%d], backlog=[%d]\n",
125                lsock, bklog) );
126    }
127
128    return rc;
129}
130
131
132/* add or drop membership in a multicast group
133 */
134int
135set_multicast( int msockfd, const struct in_addr* mifaddr,
136               int opname )
137{
138    struct sockaddr_in addr;
139    a_socklen_t len = sizeof(addr);
140    struct ip_mreq mreq;
141
142    int rc = 0;
143    const char *opstr =
144        ((IP_DROP_MEMBERSHIP == opname) ? "DROP" :
145         ((IP_ADD_MEMBERSHIP == opname) ? "ADD" : ""));
146    assert( opstr[0] );
147
148    assert( (msockfd > 0) && mifaddr );
149
150    (void) memset( &mreq, 0, sizeof(mreq) );
151    (void) memcpy( &mreq.imr_interface, mifaddr,
152                sizeof(struct in_addr) );
153
154    (void) memset( &addr, 0, sizeof(addr) );
155    rc = getsockname( msockfd, (struct sockaddr*)&addr, &len );
156    if( 0 != rc ) {
157        mperror( g_flog, errno, "%s: getsockname", __func__ );
158        return -1;
159    }
160
161    (void) memcpy( &mreq.imr_multiaddr, &addr.sin_addr,
162                sizeof(struct in_addr) );
163
164    rc = setsockopt( msockfd, IPPROTO_IP, opname,
165                    &mreq, sizeof(mreq) );
166    if( 0 != rc ) {
167        mperror( g_flog, errno, "%s: setsockopt MCAST option: %s",
168                    __func__, opstr );
169        return rc;
170    }
171
172    TRACE( (void)tmfprintf( g_flog, "multicast-group [%s]\n",
173                            opstr ) );
174    return rc;
175}
176
177
178/* set up the socket to receive multicast data
179 *
180 */
181int
182setup_mcast_listener( struct sockaddr_in*   sa,
183                      const struct in_addr* mifaddr,
184                      int*                  mcastfd,
185                      int                   sockbuflen )
186{
187    int sockfd, rc;
188    int ON = 1;
189    int buflen = sockbuflen;
190    size_t rcvbuf_len = 0;
191
192    assert( sa && mifaddr && mcastfd && (sockbuflen >= 0) );
193
194    TRACE( (void)tmfprintf( g_flog, "Setting up multicast listener\n") );
195    rc = ERR_INTERNAL;
196    do {
197        sockfd = socket( AF_INET, SOCK_DGRAM, 0 );
198        if( -1 == sockfd ) {
199            mperror(g_flog, errno, "%s: socket", __func__);
200            break;
201        }
202
203        if (buflen != 0) {
204            rc = get_rcvbuf( sockfd, &rcvbuf_len );
205            if (0 != rc) break;
206
207            if ((size_t)buflen > rcvbuf_len) {
208                rc = set_rcvbuf( sockfd, buflen );
209                if (0 != rc) break;
210            }
211        }
212        else {
213            TRACE( (void)tmfprintf( g_flog, "Must not adjust buffer size "
214                                            "for mcast socket [%d]\n", sockfd ) );
215        }
216
217        rc = setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR,
218                         &ON, sizeof(ON) );
219        if( 0 != rc ) {
220            mperror(g_flog, errno, "%s: setsockopt SO_REUSEADDR",
221                    __func__);
222            break;
223        }
224
225#ifdef SO_REUSEPORT
226        /*  On some systems (such as FreeBSD) SO_REUSEADDR
227            just isn't enough to subscribe to N same channels for different clients.
228        */
229        rc = setsockopt( sockfd, SOL_SOCKET, SO_REUSEPORT,
230                         &ON, sizeof(ON) );
231        if( 0 != rc ) {
232            mperror(g_flog, errno, "%s: setsockopt SO_REUSEPORT",
233                    __func__);
234            break;
235        }
236#endif /* SO_REUSEPORT */
237
238        rc = bind( sockfd, (struct sockaddr*)sa, sizeof(*sa) );
239        if( 0 != rc ) {
240            mperror(g_flog, errno, "%s: bind", __func__);
241            break;
242        }
243
244        rc = set_multicast( sockfd, mifaddr, IP_ADD_MEMBERSHIP );
245        if( 0 != rc )
246            break;
247    } while(0);
248
249    if( 0 == rc ) {
250        *mcastfd = sockfd;
251        TRACE( (void)tmfprintf( g_flog, "Mcast listener socket=[%d] set up\n",
252                                sockfd) );
253    }
254    else {
255        (void)close(sockfd);
256    }
257
258    return rc;
259}
260
261
262/* unsubscribe from multicast and close the reader socket
263 */
264void
265close_mcast_listener( int msockfd, const struct in_addr* mifaddr )
266{
267    assert( mifaddr );
268
269    if( msockfd <= 0 ) return;
270
271    (void) set_multicast( msockfd, mifaddr, IP_DROP_MEMBERSHIP );
272    (void) close( msockfd );
273
274    TRACE( (void)tmfprintf( g_flog, "Mcast listener socket=[%d] closed\n",
275                msockfd) );
276    return;
277}
278
279
280/* drop from and add into a multicast group
281 */
282int
283renew_multicast( int msockfd, const struct in_addr* mifaddr )
284{
285    int rc = 0;
286
287    rc = set_multicast( msockfd, mifaddr, IP_DROP_MEMBERSHIP );
288    if( 0 != rc ) return rc;
289
290    rc = set_multicast( msockfd, mifaddr, IP_ADD_MEMBERSHIP );
291    return rc;
292}
293
294
295/* set send/receive timeouts on socket(s)
296 */
297int
298set_timeouts( int rsock, int ssock,
299              u_short rcv_tmout_sec, u_short rcv_tmout_usec,
300              u_short snd_tmout_sec, u_short snd_tmout_usec )
301{
302    struct timeval rtv, stv;
303    int rc = 0;
304
305    if( rsock ) {
306        rtv.tv_sec = rcv_tmout_sec;
307        rtv.tv_usec = rcv_tmout_usec;
308        rc = setsockopt( rsock, SOL_SOCKET, SO_RCVTIMEO, &rtv, sizeof(rtv) );
309        if( -1 == rc ) {
310            mperror(g_flog, errno, "%s: setsockopt - SO_RCVTIMEO",
311                    __func__);
312            return ERR_INTERNAL;
313        }
314        TRACE( (void)tmfprintf (g_flog, "socket %d: RCV timeout set to %ld sec, %ld usec\n",
315                rsock, rtv.tv_sec, rtv.tv_usec) );
316    }
317
318    if( ssock ) {
319        stv.tv_sec = snd_tmout_sec;
320        stv.tv_usec = snd_tmout_usec;
321        rc = setsockopt( ssock, SOL_SOCKET, SO_SNDTIMEO, &stv, sizeof(stv) );
322        if( -1 == rc ) {
323            mperror(g_flog, errno, "%s: setsockopt - SO_SNDTIMEO", __func__);
324            return ERR_INTERNAL;
325        }
326        TRACE( (void)tmfprintf (g_flog, "socket %d: SEND timeout set to %ld sec, %ld usec\n",
327                rsock, rtv.tv_sec, rtv.tv_usec) );
328    }
329
330    return rc;
331}
332
333
334
335/* set socket's send/receive buffer size
336 */
337static int
338set_sockbuf_size( int sockfd, int option, const size_t len,
339        const char* bufname )
340{
341    size_t data_len = len;
342    int rc = 0;
343
344    assert( bufname );
345
346    rc = setsockopt( sockfd, SOL_SOCKET, option,
347                    &data_len, sizeof(data_len) );
348    if( 0 != rc ) {
349        mperror(g_flog, errno, "%s: setsockopt %s [%d]",
350                __func__, bufname, option);
351        return -1;
352    }
353    else {
354        TRACE( (void)tmfprintf( g_flog,
355            "%s buffer size set to [%u] bytes for socket [%d]\n",
356            bufname, data_len, sockfd ) );
357    }
358
359    return rc;
360}
361
362
363/* set socket's send buffer value
364 */
365int
366set_sendbuf( int sockfd, const size_t len )
367{
368    return set_sockbuf_size( sockfd, SO_SNDBUF, len, "send" );
369}
370
371
372/* set socket's send buffer value
373 */
374int
375set_rcvbuf( int sockfd, const size_t len )
376{
377    return set_sockbuf_size( sockfd, SO_RCVBUF, len, "receive" );
378}
379
380
381static int
382get_sockbuf_size( int sockfd, int option, size_t* const len,
383                  const char* bufname )
384{
385    int rc = 0;
386    size_t buflen = 0;
387    socklen_t varsz = sizeof(buflen);
388
389    assert( sockfd && len && bufname );
390
391    rc = getsockopt( sockfd, SOL_SOCKET, option, &buflen, &varsz );
392    if (0 != rc) {
393        mperror( g_flog, errno, "%s: getsockopt (%s) [%d]", __func__,
394                bufname, option);
395        return -1;
396    }
397    else {
398        TRACE( (void)tmfprintf( g_flog,
399            "current %s buffer size is [%u] bytes for socket [%d]\n",
400            bufname, buflen, sockfd ) );
401        *len = buflen;
402    }
403
404    return rc;
405
406}
407
408/* get socket's send buffer size
409 */
410int
411get_sendbuf( int sockfd, size_t* const len )
412{
413    return get_sockbuf_size( sockfd, SO_SNDBUF, len, "send" );
414}
415
416
417/* get socket's receive buffer size
418 */
419int
420get_rcvbuf( int sockfd, size_t* const len )
421{
422    return get_sockbuf_size( sockfd, SO_RCVBUF, len, "receive" );
423}
424
425
426
427/* set/clear file/socket's mode as non-blocking
428 */
429int
430set_nblock( int fd, int set )
431{
432    int flags = 0;
433
434    flags = fcntl( fd, F_GETFL, 0 );
435    if( flags < 0 ) {
436        mperror( g_flog, errno, "%s: fcntl() getting flags on fd=[%d]",
437                    __func__, fd );
438        return -1;
439    }
440
441    if( set )
442        flags |= O_NONBLOCK;
443    else
444        flags &= ~O_NONBLOCK;
445
446    if( fcntl( fd, F_SETFL, flags ) < 0 ) {
447        mperror( g_flog, errno, "%s: fcntl() %s non-blocking mode "
448                "on fd=[%d]", __func__, (set ? "setting" : "clearing"),
449                fd );
450        return -1;
451    }
452
453    return 0;
454}
455
456
457static int
458sock_info (int peer, int sockfd, char* addr, size_t alen, int* port)
459{
460    int rc = 0;
461    union {
462        struct sockaddr sa;
463        char data [sizeof(struct sockaddr_in6)];
464    } gsa;
465    socklen_t len;
466
467    const void* dst = NULL;
468    struct sockaddr_in6 *sa6 = NULL;
469    struct sockaddr_in  *sa4 = NULL;
470
471    len = sizeof (gsa.data);
472    rc = peer ? getpeername (sockfd, (struct sockaddr*)gsa.data, &len):
473                getsockname (sockfd, (struct sockaddr*)gsa.data, &len);
474    if (0 != rc) {
475        rc = errno;
476        (void) fprintf (g_flog, "getsockname error [%d]: %s\n",
477            rc, strerror (rc));
478        return rc;
479    }
480
481    switch (gsa.sa.sa_family) {
482        case AF_INET:
483            sa4 = (struct sockaddr_in*)&gsa.sa;
484            if (addr) {
485                dst = inet_ntop (gsa.sa.sa_family, &(sa4->sin_addr),
486                    addr, (socklen_t)alen);
487            }
488            if (port) *port = (int) ntohs (sa4->sin_port);
489            break;
490        case AF_INET6:
491            sa6 = (struct sockaddr_in6*)&gsa.sa;
492            if (addr) {
493                dst = inet_ntop (gsa.sa.sa_family, &(sa6->sin6_addr),
494                    addr, (socklen_t)alen);
495            }
496            if (port) *port = (int) ntohs (sa6->sin6_port);
497            break;
498        default:
499            (void) tmfprintf (g_flog, "%s: unsupported socket family: %d\n",
500                __func__, (int)gsa.sa.sa_family);
501            return EAFNOSUPPORT;
502    }
503    if (addr && !dst) {
504        rc = errno;
505        (void) tmfprintf (g_flog, "%s: inet_pton error [%d]: %s\n",
506            __func__, rc, strerror (rc));
507        return rc;
508    }
509
510    return rc;
511}
512
513enum {e_host = 0, e_peer = 1};
514int
515get_sockinfo (int sockfd, char* addr, size_t alen, int* port)
516{
517    return sock_info (e_host, sockfd, addr, alen, port);
518}
519
520int
521get_peerinfo (int sockfd, char* addr, size_t alen, int* port)
522{
523    return sock_info (e_peer, sockfd, addr, alen, port);
524}
525
526
527/* __EOF__ */
528
529