Switch to unified view

a b/src/netcon.cpp
1
/* Copyright (C) 2002 J.F.Dockes
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the
14
 *   Free Software Foundation, Inc.,
15
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16
 */
17
18
// Wrapper classes for the socket interface
19
20
21
#ifndef TEST_NETCON
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <errno.h>
26
27
#ifdef _AIX
28
#include <strings.h>
29
#endif // _AIX
30
31
#include <unistd.h>
32
#include <fcntl.h>
33
#include <sys/time.h>
34
35
#include <sys/socket.h>
36
#include <netinet/in.h>
37
#include <netinet/tcp.h>
38
#include <arpa/inet.h>
39
#include <netdb.h>
40
41
#include <map>
42
43
using namespace std;
44
45
#ifdef HAVE_DEBUGLOG
46
#include "debuglog.h"
47
48
#else
49
50
#define LOGFATAL(X)
51
#define LOGERR(X)
52
#define LOGINFO(X)
53
#define LOGDEB(X)
54
#define LOGDEB0(X)
55
#define LOGDEB1(X)
56
#define LOGDEB2(X)
57
#define LOGDEB3(X)
58
#define LOGDEB4(X)
59
#endif
60
61
#include "netcon.h"
62
63
#ifndef SOCKLEN_T
64
#define SOCKLEN_T socklen_t
65
#endif
66
67
// Need &one, &zero for setsockopt...
68
static const int one = 1;
69
static const int zero = 0;
70
71
#define LOGSYSERR(who, call, spar)                     \
72
    LOGERR(("%s: %s(%s) errno %d (%s)\n", who, call,   \
73
        spar, errno, strerror(errno)))
74
75
#ifndef MIN
76
#define MIN(a,b) (a<b?a:b)
77
#endif
78
#ifndef MAX
79
#define MAX(a,b) (a>b?a:b)
80
#endif
81
#ifndef freeZ
82
#define freeZ(X) if (X) {free(X);X=0;}
83
#endif
84
85
#define MILLIS(OLD, NEW) ( (long)(((NEW).tv_sec - (OLD).tv_sec) * 1000 + \
86
                  ((NEW).tv_usec - (OLD).tv_usec) / 1000))
87
88
// Static method
89
// Simplified interface to 'select()'. Only use one fd, for either
90
// reading or writing. This is only used when not using the
91
// selectloop() style of network i/o.
92
// Note that timeo == 0 does NOT mean wait forever but no wait at all.
93
int Netcon::select1(int fd, int timeo, int write)
94
{
95
    int ret;
96
    struct timeval tv;
97
    fd_set rd;
98
    tv.tv_sec = timeo;
99
    tv.tv_usec =  0;
100
    FD_ZERO(&rd);
101
    FD_SET(fd, &rd);
102
    if (write) {
103
        ret = select(fd + 1, 0, &rd, 0, &tv);
104
    } else {
105
        ret = select(fd + 1, &rd, 0, 0, &tv);
106
    }
107
    if (!FD_ISSET(fd, &rd)) {
108
        LOGERR(("Netcon::select1: fd not ready after select ??\n"));
109
        return -1;
110
    }
111
    return ret;
112
}
113
114
void SelectLoop::setperiodichandler(int (*handler)(void *), void *p, int ms)
115
{
116
    m_periodichandler = handler;
117
    m_periodicparam = p;
118
    m_periodicmillis = ms;
119
    if (m_periodicmillis > 0) {
120
        gettimeofday(&m_lasthdlcall, 0);
121
    }
122
}
123
124
// Compute the appropriate timeout so that the select call returns in
125
// time to call the periodic routine.
126
void SelectLoop::periodictimeout(struct timeval *tv)
127
{
128
    // If periodic not set, the select call times out and we loop
129
    // after a very long time (we'd need to pass NULL to select for an
130
    // infinite wait, and I'm too lazy to handle it)
131
    if (m_periodicmillis <= 0) {
132
        tv->tv_sec = 10000;
133
        tv->tv_usec = 0;
134
        return;
135
    }
136
137
    struct timeval mtv;
138
    gettimeofday(&mtv, 0);
139
    int millis = m_periodicmillis - MILLIS(m_lasthdlcall, mtv);
140
141
    // millis <= 0 means we should have already done the thing. *dont* set the
142
    // tv to 0, which means no timeout at all !
143
    if (millis <= 0) {
144
        millis = 1;
145
    }
146
    tv->tv_sec = millis / 1000;
147
    tv->tv_usec = (millis % 1000) * 1000;
148
}
149
150
// Check if it's time to call the handler. selectloop will return to
151
// caller if it or we return 0
152
int SelectLoop::maybecallperiodic()
153
{
154
    if (m_periodicmillis <= 0) {
155
        return 1;
156
    }
157
    struct timeval mtv;
158
    gettimeofday(&mtv, 0);
159
    int millis = m_periodicmillis - MILLIS(m_lasthdlcall, mtv);
160
    if (millis <= 0) {
161
        gettimeofday(&m_lasthdlcall, 0);
162
        if (m_periodichandler) {
163
            return m_periodichandler(m_periodicparam);
164
        } else {
165
            return 0;
166
        }
167
    }
168
    return 1;
169
}
170
171
int SelectLoop::doLoop()
172
{
173
    for (;;) {
174
        if (m_selectloopDoReturn) {
175
            m_selectloopDoReturn = false;
176
            LOGDEB(("Netcon::selectloop: returning on request\n"));
177
            return m_selectloopReturnValue;
178
        }
179
        int nfds;
180
        fd_set rd, wd;
181
        FD_ZERO(&rd);
182
        FD_ZERO(&wd);
183
184
        // Walk the netcon map and set up the read and write fd_sets
185
        // for select()
186
        nfds = 0;
187
        for (map<int, NetconP>::iterator it = m_polldata.begin();
188
                it != m_polldata.end(); it++) {
189
            NetconP& pll = it->second;
190
            int fd  = it->first;
191
            LOGDEB2(("Selectloop: fd %d flags 0x%x\n", fd, pll->m_wantedEvents));
192
            if (pll->m_wantedEvents & Netcon::NETCONPOLL_READ) {
193
                FD_SET(fd, &rd);
194
                nfds = MAX(nfds, fd + 1);
195
            }
196
            if (pll->m_wantedEvents & Netcon::NETCONPOLL_WRITE) {
197
                FD_SET(fd, &wd);
198
                nfds = MAX(nfds, fd + 1);
199
            }
200
        }
201
202
        if (nfds == 0) {
203
            // This should never happen in a server as we should at least
204
            // always monitor the main listening server socket. For a
205
            // client, it's up to client code to avoid or process this
206
            // condition.
207
208
            // Just in case there would still be open fds in there
209
            // (with no r/w flags set). Should not be needed, but safer
210
            m_polldata.clear();
211
            LOGDEB1(("Netcon::selectloop: no fds\n"));
212
            return 0;
213
        }
214
215
        LOGDEB2(("Netcon::selectloop: selecting, nfds = %d\n", nfds));
216
217
        // Compute the next timeout according to what might need to be
218
        // done apart from waiting for data
219
        struct timeval tv;
220
        periodictimeout(&tv);
221
        // Wait for something to happen
222
        int ret = select(nfds, &rd, &wd, 0, &tv);
223
        LOGDEB2(("Netcon::selectloop: select returns %d\n", ret));
224
        if (ret < 0) {
225
            LOGSYSERR("Netcon::selectloop", "select", "");
226
            return -1;
227
        }
228
        if (m_periodicmillis > 0)
229
            if (maybecallperiodic() <= 0) {
230
                return 1;
231
            }
232
233
        // Timeout, do it again.
234
        if (ret == 0) {
235
            continue;
236
        }
237
238
        // We don't start the fd sweep at 0, else some fds would be advantaged.
239
        // Note that we do an fd sweep, not a map sweep. This is
240
        // inefficient because the fd array may be very sparse. Otoh, the
241
        // map may change between 2 sweeps, so that we'd have to be smart
242
        // with the iterator. As the cost per unused fd is low (just 2 bit
243
        // flag tests), we keep it like this for now
244
        if (m_placetostart >= nfds) {
245
            m_placetostart = 0;
246
        }
247
        int i, fd;
248
        for (i = 0, fd = m_placetostart; i < nfds; i++, fd++) {
249
            if (fd >= nfds) {
250
                fd = 0;
251
            }
252
253
            int canread = FD_ISSET(fd, &rd);
254
            int canwrite = FD_ISSET(fd, &wd);
255
            bool none = !canread && !canwrite;
256
            LOGDEB2(("Netcon::selectloop: fd %d %s %s %s\n", fd,
257
                     none ? "blocked" : "can" , canread ? "read" : "",
258
                     canwrite ? "write" : ""));
259
            if (none) {
260
                continue;
261
            }
262
263
            map<int, NetconP>::iterator it = m_polldata.find(fd);
264
            if (it == m_polldata.end()) {
265
                /// This should not happen actually
266
                LOGDEB2(("Netcon::selectloop: fd %d not found\n", fd));
267
                continue;
268
            }
269
270
            // Next start will be one beyond last serviced (modulo nfds)
271
            m_placetostart = fd + 1;
272
            NetconP& pll = it->second;
273
            if (canread && pll->cando(Netcon::NETCONPOLL_READ) <= 0) {
274
                pll->m_wantedEvents &= ~Netcon::NETCONPOLL_READ;
275
            }
276
            if (canwrite && pll->cando(Netcon::NETCONPOLL_WRITE) <= 0) {
277
                pll->m_wantedEvents &= ~Netcon::NETCONPOLL_WRITE;
278
            }
279
            if (!(pll->m_wantedEvents & (Netcon::NETCONPOLL_WRITE | Netcon::NETCONPOLL_READ))) {
280
                LOGDEB0(("Netcon::selectloop: fd %d has 0x%x mask, erasing\n",
281
                         it->first, it->second->m_wantedEvents));
282
                m_polldata.erase(it);
283
            }
284
        } // fd sweep
285
286
    } // forever loop
287
    LOGERR(("SelectLoop::doLoop: got out of loop !\n"));
288
    return -1;
289
}
290
291
// Add a connection to the monitored set.
292
int SelectLoop::addselcon(NetconP con, int events)
293
{
294
    if (!con) {
295
        return -1;
296
    }
297
    LOGDEB1(("Netcon::addselcon: fd %d\n", con->m_fd));
298
    con->set_nonblock(1);
299
    con->setselevents(events);
300
    m_polldata[con->m_fd] = con;
301
    con->setloop(this);
302
    return 0;
303
}
304
305
// Remove a connection from the monitored set.
306
int SelectLoop::remselcon(NetconP con)
307
{
308
    if (!con) {
309
        return -1;
310
    }
311
    LOGDEB1(("Netcon::remselcon: fd %d\n", con->m_fd));
312
    map<int, NetconP>::iterator it = m_polldata.find(con->m_fd);
313
    if (it == m_polldata.end()) {
314
        LOGDEB1(("Netcon::remselcon: con not found for fd %d\n", con->m_fd));
315
        return -1;
316
    }
317
    con->setloop(0);
318
    m_polldata.erase(it);
319
    return 0;
320
}
321
322
//////////////////////////////////////////////////////////
323
// Base class (Netcon) methods
324
Netcon::~Netcon()
325
{
326
    closeconn();
327
    if (m_peer) {
328
        free(m_peer);
329
        m_peer = 0;
330
    }
331
}
332
333
void Netcon::closeconn()
334
{
335
    if (m_ownfd && m_fd >= 0) {
336
        close(m_fd);
337
    }
338
    m_fd = -1;
339
    m_ownfd = true;
340
}
341
342
char *Netcon::sterror()
343
{
344
    return strerror(errno);
345
}
346
347
void Netcon::setpeer(const char *hostname)
348
{
349
    if (m_peer) {
350
        free(m_peer);
351
    }
352
    m_peer = strdup(hostname);
353
}
354
355
int Netcon::settcpnodelay(int on)
356
{
357
    LOGDEB2(("Netcon::settcpnodelay\n"));
358
    if (m_fd < 0) {
359
        LOGERR(("Netcon::settcpnodelay: connection not opened\n"));
360
        return -1;
361
    }
362
    char *cp = on ? (char *)&one : (char *)&zero;
363
    if (setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, cp, sizeof(one)) < 0) {
364
        LOGSYSERR("NetconCli::settcpnodelay", "setsockopt", "TCP_NODELAY");
365
        return -1;
366
    }
367
    return 0;
368
}
369
370
// Set/reset non-blocking flag on fd
371
int Netcon::set_nonblock(int onoff)
372
{
373
    int  flags = fcntl(m_fd, F_GETFL, 0);
374
    if (flags != -1)   {
375
        int newflags = onoff ? flags | O_NONBLOCK : flags & ~O_NONBLOCK;
376
        if (newflags != flags)
377
            if (fcntl(m_fd, F_SETFL, newflags) < 0) {
378
                return -1;
379
            }
380
    }
381
    return flags;
382
}
383
384
/////////////////////////////////////////////////////////////////////
385
// Data socket (NetconData) methods
386
387
NetconData::~NetconData()
388
{
389
    freeZ(m_buf);
390
    m_bufbase = 0;
391
    m_bufbytes = m_bufsize = 0;
392
}
393
394
int NetconData::send(const char *buf, int cnt, int expedited)
395
{
396
    LOGDEB2(("NetconData::send: fd %d cnt %d expe %d\n", m_fd, cnt, expedited));
397
    int flag = 0;
398
    if (m_fd < 0) {
399
        LOGERR(("NetconData::send: connection not opened\n"));
400
        return -1;
401
    }
402
    if (expedited) {
403
        LOGDEB2(("NetconData::send: expedited data, count %d bytes\n", cnt));
404
        flag = MSG_OOB;
405
    }
406
    int ret;
407
    // There is a bug in the uthread version of sendto() in FreeBSD at
408
    // least up to 2.2.7, so avoid using it when possible
409
    if (flag) {
410
        ret = ::send(m_fd, buf, cnt, flag);
411
    } else {
412
        ret = ::write(m_fd, buf, cnt);
413
    }
414
415
    // Note: byte count may be different from cnt if fd is non-blocking
416
    if (ret < 0) {
417
        char fdcbuf[20];
418
        sprintf(fdcbuf, "%d", m_fd);
419
        LOGSYSERR("NetconData::send", "send", fdcbuf);
420
    }
421
    return ret;
422
}
423
424
// Test for data available
425
int NetconData::readready()
426
{
427
    LOGDEB2(("NetconData::readready\n"));
428
    if (m_fd < 0) {
429
        LOGERR(("NetconData::readready: connection not opened\n"));
430
        return -1;
431
    }
432
    return select1(m_fd, 0);
433
}
434
435
// Test for writable
436
int NetconData::writeready()
437
{
438
    LOGDEB2(("NetconData::writeready\n"));
439
    if (m_fd < 0) {
440
        LOGERR(("NetconData::writeready: connection not opened\n"));
441
        return -1;
442
    }
443
    return select1(m_fd, 0, 1);
444
}
445
446
// Receive at most cnt bytes (maybe less)
447
int NetconData::receive(char *buf, int cnt, int timeo)
448
{
449
    LOGDEB2(("NetconData::receive: cnt %d timeo %d m_buf 0x%x m_bufbytes %d\n",
450
             cnt, timeo, m_buf, m_bufbytes));
451
    if (m_fd < 0) {
452
        LOGERR(("NetconData::receive: connection not opened\n"));
453
        return -1;
454
    }
455
    int fromibuf = 0;
456
    // Get whatever might have been left in the buffer by a previous
457
    // getline, except if we're called to fill the buffer of course
458
    if (m_buf && m_bufbytes > 0 && (buf < m_buf || buf > m_buf + m_bufsize)) {
459
        fromibuf = MIN(m_bufbytes, cnt);
460
        memcpy(buf, m_bufbase, fromibuf);
461
        m_bufbytes -= fromibuf;
462
        m_bufbase += fromibuf;
463
        cnt -= fromibuf;
464
        LOGDEB2(("NetconData::receive: transferred %d from mbuf\n", fromibuf));
465
        if (cnt <= 0) {
466
            return fromibuf;
467
        }
468
    }
469
    if (timeo > 0) {
470
        int ret = select1(m_fd, timeo);
471
        if (ret == 0) {
472
            LOGDEB2(("NetconData::receive timed out\n"));
473
            m_didtimo = 1;
474
            return -1;
475
        }
476
        if (ret < 0) {
477
            LOGSYSERR("NetconData::receive", "select", "");
478
            return -1;
479
        }
480
    }
481
    m_didtimo = 0;
482
    if ((cnt = read(m_fd, buf + fromibuf, cnt)) < 0) {
483
        char fdcbuf[20];
484
        sprintf(fdcbuf, "%d", m_fd);
485
        LOGSYSERR("NetconData::receive", "read", fdcbuf);
486
        return -1;
487
    }
488
    LOGDEB2(("NetconData::receive: normal return, cnt %d\n", cnt));
489
    return fromibuf + cnt;
490
}
491
492
// Receive exactly cnt bytes (except for timeout)
493
int NetconData::doreceive(char *buf, int cnt, int timeo)
494
{
495
    int got, cur;
496
    LOGDEB2(("Netcon::doreceive: cnt %d, timeo %d\n", cnt, timeo));
497
    cur = 0;
498
    while (cnt > cur) {
499
        got = receive(buf, cnt - cur, timeo);
500
        LOGDEB2(("Netcon::doreceive: got %d\n", got));
501
        if (got < 0) {
502
            return -1;
503
        }
504
        if (got == 0) {
505
            return cur;
506
        }
507
        cur += got;
508
        buf += got;
509
    }
510
    return cur;
511
}
512
513
// Read data until cnt-1 characters are read or a newline is found. Add
514
// null char at end of buffer and return.
515
// As we don't know where the newline will be and it would be inefficient to
516
// read a character at a time, we use a buffer
517
// Unlike fgets, we return an integer status:
518
// >0: number of characters returned, not including the final 0
519
//  0: EOF reached, no chars transferred
520
// -1: error
521
static const int defbufsize = 200;
522
int NetconData::getline(char *buf, int cnt, int timeo)
523
{
524
    LOGDEB2(("NetconData::getline: cnt %d, timeo %d\n", cnt, timeo));
525
    if (m_buf == 0) {
526
        if ((m_buf = (char *)malloc(defbufsize)) == 0) {
527
            LOGSYSERR("NetconData::getline: Out of mem", "malloc", "");
528
            return -1;
529
        }
530
        m_bufsize = defbufsize;
531
        m_bufbase = m_buf;
532
        m_bufbytes = 0;
533
    }
534
535
    char *cp = buf;
536
    for (;;) {
537
        // Transfer from buffer. Have to take a lot of care to keep counts and
538
        // pointers consistant in all end cases
539
        int maxtransf = MIN(m_bufbytes, cnt - 1);
540
        int nn = maxtransf;
541
        LOGDEB2(("Before loop, bufbytes %d, maxtransf %d, nn: %d\n",
542
                 m_bufbytes, maxtransf, nn));
543
        for (nn = maxtransf; nn > 0;) {
544
            // This is not pretty but we want nn to be decremented for
545
            // each byte copied (even newline), and not become -1 if
546
            // we go to the end. Better ways welcome!
547
            nn--;
548
            if ((*cp++ = *m_bufbase++) == '\n') {
549
                break;
550
            }
551
        }
552
        // Update counts
553
        maxtransf -= nn; // Actual count transferred
554
        m_bufbytes -= maxtransf;
555
        cnt -= maxtransf;
556
        LOGDEB2(("After transfer: actual transf %d cnt %d, m_bufbytes %d\n",
557
                 maxtransf, cnt, m_bufbytes));
558
559
        // Finished ?
560
        if (cnt <= 1 || (cp > buf && cp[-1] == '\n')) {
561
            *cp = 0;
562
            return cp - buf;
563
        }
564
565
        // Transfer from net
566
        m_bufbase = m_buf;
567
        m_bufbytes = receive(m_buf, m_bufsize, timeo);
568
        if (m_bufbytes == 0) {
569
            // EOF
570
            *cp = 0;
571
            return cp - buf;
572
        }
573
        if (m_bufbytes < 0) {
574
            m_bufbytes = 0;
575
            *cp = 0;
576
            return -1;
577
        }
578
    }
579
}
580
581
// Called when selectloop detects that data can be read or written on
582
// the connection. The user callback would normally have been set
583
// up. If it is, call it and return. Else, perform housecleaning: read
584
// and discard.
585
int NetconData::cando(Netcon::Event reason)
586
{
587
    LOGDEB2(("NetconData::cando\n"));
588
    if (m_user) {
589
        return m_user->data(this, reason);
590
    }
591
592
    // No user callback. Clean up by ourselves
593
    if (reason & NETCONPOLL_READ) {
594
#define BS 200
595
        char buf[BS];
596
        int n;
597
        if ((n = receive(buf, BS)) < 0) {
598
            LOGSYSERR("NetconData::cando", "receive", "");
599
            return -1;
600
        }
601
        if (n == 0) {
602
            // EOF
603
            return 0;
604
        }
605
    }
606
    clearselevents(NETCONPOLL_WRITE);
607
    return 1;
608
}
609
610
///////////////////////////////////////////////////////////////////////
611
// Methods for a client connection (NetconCli)
612
int NetconCli::openconn(const char *host, unsigned int port, int timeo)
613
{
614
    int ret = -1;
615
    LOGDEB2(("Netconcli::openconn: host %s, port %d\n", host, port));
616
617
    closeconn();
618
619
    struct sockaddr_in saddr;
620
    memset(&saddr, 0, sizeof(saddr));
621
    saddr.sin_family = AF_INET;
622
    saddr.sin_port = htons(port);
623
624
    // Server name may be host name or IP address
625
    int addr;
626
    if ((addr = inet_addr(host)) != -1) {
627
        memcpy(&saddr.sin_addr, &addr, sizeof(addr));
628
    } else {
629
        struct hostent *hp;
630
        if ((hp = gethostbyname(host)) == 0) {
631
            LOGERR(("NetconCli::openconn: gethostbyname(%s) failed\n", host));
632
            return -1;
633
        }
634
        memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length);
635
    }
636
637
    if ((m_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
638
        LOGSYSERR("NetconCli::openconn", "socket", "");
639
        return -1;
640
    }
641
    if (timeo > 0) {
642
        set_nonblock(1);
643
    }
644
645
    if (connect(m_fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
646
        if (timeo > 0) {
647
            if (errno != EINPROGRESS) {
648
                goto out;
649
            }
650
            if (select1(m_fd, timeo, 1) == 1) {
651
                goto connectok;
652
            }
653
        }
654
        if (m_silentconnectfailure == 0) {
655
            LOGSYSERR("NetconCli", "connect", "");
656
        }
657
        goto out;
658
    }
659
connectok:
660
    if (timeo > 0) {
661
        set_nonblock(0);
662
    }
663
664
    LOGDEB2(("NetconCli::connect: setting keepalive\n"));
665
    if (setsockopt(m_fd, SOL_SOCKET, SO_KEEPALIVE,
666
                   (char *)&one, sizeof(one)) < 0) {
667
        LOGSYSERR("NetconCli::connect", "setsockopt", "KEEPALIVE");
668
    }
669
    setpeer(host);
670
    LOGDEB2(("NetconCli::openconn: connection opened ok\n"));
671
    ret = 0;
672
out:
673
    if (ret < 0) {
674
        closeconn();
675
    }
676
    return ret;
677
}
678
679
// Same as previous, but get the port number from services
680
int NetconCli::openconn(const char *host, char *serv, int timeo)
681
{
682
    LOGDEB2(("Netconcli::openconn: host %s, serv %s\n", host, serv));
683
684
    struct servent *sp;
685
    if ((sp = getservbyname(serv, "tcp")) == 0) {
686
        LOGERR(("NetconCli::openconn: getservbyname failed for %s\n", serv));
687
        return -1;
688
    }
689
    // Callee expects the port number in host byte order
690
    return openconn(host, ntohs(sp->s_port), timeo);
691
}
692
693
694
int NetconCli::setconn(int fd)
695
{
696
    LOGDEB2(("Netconcli::setconn: fd %d\n", fd));
697
    closeconn();
698
699
    m_fd = fd;
700
    m_ownfd = false;
701
    setpeer("");
702
703
    return 0;
704
}
705
706
///////////////////////////////////////////////////////////////////////
707
// Methods for the main (listening) server connection
708
709
NetconServLis::~NetconServLis()
710
{
711
#ifdef NETCON_ACCESSCONTROL
712
    freeZ(okaddrs.intarray);
713
    freeZ(okmasks.intarray);
714
#endif
715
}
716
717
#if 0
718
// code for dumping a struct servent
719
static void dump_servent(struct servent *servp)
720
{
721
    fprintf(stderr, "Official name %s\n", servp->s_name);
722
    for (char **cpp = servp->s_aliases; *cpp; cpp++) {
723
        fprintf(stderr, "Nickname %s\n", *cpp);
724
    }
725
    fprintf(stderr, "Port %d\n", (int)ntohs((short)servp->s_port));
726
    fprintf(stderr, "Proto %s\n", servp->s_proto);
727
}
728
#endif
729
730
// Set up service.
731
int NetconServLis::openservice(char *serv, int backlog)
732
{
733
    int port;
734
    struct servent  *servp;
735
    LOGDEB1(("NetconServLis::openservice: serv %s\n", serv));
736
#ifdef NETCON_ACCESSCONTROL
737
    if (initperms(serv) < 0) {
738
        return -1;
739
    }
740
#endif
741
    if ((servp = getservbyname(serv, "tcp")) == 0) {
742
        LOGERR(("NetconServLis::openservice: getservbyname failed for %s\n", serv));
743
        return -1;
744
    }
745
    port = (int)ntohs((short)servp->s_port);
746
    return openservice(port, backlog);
747
}
748
749
// Port is a natural host integer value
750
int NetconServLis::openservice(int port, int backlog)
751
{
752
    LOGDEB1(("NetconServLis::openservice: port %d\n", port));
753
#ifdef NETCON_ACCESSCONTROL
754
    if (initperms(port) < 0) {
755
        return -1;
756
    }
757
#endif
758
    int ret = -1;
759
    struct sockaddr_in  ipaddr;
760
    if ((m_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
761
        LOGSYSERR("NetconServLis", "socket", "");
762
        return -1;
763
    }
764
    (void) setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
765
#ifdef SO_REUSEPORT
766
    (void) setsockopt(m_fd, SOL_SOCKET, SO_REUSEPORT, (char *)&one, sizeof(one));
767
#endif /*SO_REUSEPORT*/
768
    memset(&ipaddr, 0, sizeof(ipaddr));
769
    ipaddr.sin_family = AF_INET;
770
    ipaddr.sin_addr.s_addr = htonl(INADDR_ANY);
771
    ipaddr.sin_port = htons((short)port);
772
    if (bind(m_fd, (struct sockaddr *)&ipaddr, sizeof(ipaddr)) < 0) {
773
        LOGSYSERR("NetconServLis", "bind", "");
774
        goto out;
775
    }
776
    if (listen(m_fd, backlog) < 0) {
777
        LOGSYSERR("NetconServLis", "listen", "");
778
        goto out;
779
    }
780
781
    LOGDEB1(("NetconServLis::openservice: service opened ok\n"));
782
    ret = 0;
783
out:
784
    if (ret < 0 && m_fd >= 0) {
785
        close(m_fd);
786
        m_fd = -1;
787
    }
788
    return ret;
789
}
790
791
#ifdef NETCON_ACCESSCONTROL
792
int NetconServLis::initperms(int port)
793
{
794
    if (permsinit) {
795
        return 0;
796
    }
797
798
    char sport[30];
799
    sprintf(sport, "%d", port);
800
    return initperms(sport);
801
}
802
803
// Get authorized address lists from parameter file. This is disabled for now
804
int NetconServLis::initperms(char *serv)
805
{
806
    if (permsinit) {
807
        return 0;
808
    }
809
810
    if (serv == 0 || *serv == 0 || strlen(serv) > 80) {
811
        LOGERR(("NetconServLis::initperms: bad service name %s\n", serv));
812
        return -1;
813
    }
814
815
    char keyname[100];
816
    sprintf(keyname, "%s_okaddrs", serv);
817
    if (genparams->getparam(keyname, &okaddrs, 1) < 0) {
818
        serv = "default";
819
        sprintf(keyname, "%s_okaddrs", serv);
820
        if (genparams->getparam(keyname, &okaddrs) < 0) {
821
            LOGERR(("NetconServLis::initperms: no okaddrs found in config file\n"));
822
            return -1;
823
        }
824
    }
825
    sprintf(keyname, "%s_okmasks", serv);
826
    if (genparams->getparam(keyname, &okmasks)) {
827
        LOGERR(("NetconServLis::initperms: okmasks not found\n"));
828
        return -1;
829
    }
830
    if (okaddrs.len == 0 || okmasks.len == 0) {
831
        LOGERR(("NetconServLis::initperms: len 0 for okmasks or okaddrs\n"));
832
        return -1;
833
    }
834
835
    permsinit = 1;
836
    return 0;
837
}
838
#endif /* NETCON_ACCESSCONTROL */
839
840
// Sample cando routine for server master connection: delete newly
841
// accepted connection. What else ?
842
// This is to be overriden by a derived class method for an application
843
// using the selectloop thing
844
int  NetconServLis::cando(Netcon::Event reason)
845
{
846
    delete accept();
847
    return 1;
848
}
849
850
NetconServCon *
851
NetconServLis::accept(int timeo)
852
{
853
    LOGDEB(("NetconServLis::accept\n"));
854
855
    if (timeo > 0) {
856
        int ret = select1(m_fd, timeo);
857
        if (ret == 0) {
858
            LOGDEB2(("NetconServLis::accept timed out\n"));
859
            m_didtimo = 1;
860
            return 0;
861
        }
862
        if (ret < 0) {
863
            LOGSYSERR("NetconServLis::accept", "select", "");
864
            return 0;
865
        }
866
    }
867
    m_didtimo = 0;
868
869
    NetconServCon *con = 0;
870
    int newfd = -1;
871
    struct sockaddr_in who;
872
    SOCKLEN_T clilen = (SOCKLEN_T)sizeof(who);
873
    if ((newfd = ::accept(m_fd, (struct sockaddr *)&who, &clilen)) < 0) {
874
        LOGSYSERR("NetconServCon::accept", "accept", "");
875
        goto out;
876
    }
877
#ifdef NETCON_ACCESSCONTROL
878
    if (checkperms(&who, clilen) < 0) {
879
        goto out;
880
    }
881
#endif
882
    con = new NetconServCon(newfd);
883
    if (con == 0) {
884
        LOGERR(("NetconServLis::accept: new NetconServCon failed\n"));
885
        goto out;
886
    }
887
    // Retrieve peer's host name. Errors are non fatal
888
    struct hostent *hp;
889
    if ((hp = gethostbyaddr((char *) & (who.sin_addr), sizeof(struct in_addr),
890
                            AF_INET)) == 0) {
891
        LOGERR(("NetconServLis::accept: gethostbyaddr failed for addr 0x%lx\n",
892
                who.sin_addr.s_addr));
893
        con->setpeer(inet_ntoa(who.sin_addr));
894
    } else {
895
        con->setpeer(hp->h_name);
896
    }
897
    LOGDEB2(("NetconServLis::accept: setting keepalive\n"));
898
    if (setsockopt(newfd, SOL_SOCKET, SO_KEEPALIVE,
899
                   (char *)&one, sizeof(one)) < 0) {
900
        LOGSYSERR("NetconServLis::accept", "setsockopt", "KEEPALIVE");
901
    }
902
    LOGDEB2(("NetconServLis::accept: got connect from %s\n", con->getpeer()));
903
904
out:
905
    if (con == 0 && newfd >= 0) {
906
        close(newfd);
907
    }
908
    return con;
909
}
910
911
#ifdef NETCON_ACCESSCONTROL
912
int
913
NetconServLis::checkperms(void *cl, int)
914
{
915
    // If okmasks and addrs were not initialized, the default is allow to all
916
    if (okmasks.len <= 0 || okaddrs.len <= 0) {
917
        return 0;
918
    }
919
920
    struct sockaddr *addr = (struct sockaddr *)cl;
921
    unsigned long ip_addr;
922
923
    if (addr->sa_family != AF_INET) {
924
        LOGERR(("NetconServLis::checkperms: connection from non-INET addr !\n"));
925
        return -1;
926
    }
927
928
    ip_addr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr);
929
    LOGDEB2(("checkperms: ip_addr: 0x%x\n", ip_addr));
930
    for (int i = 0; i < okaddrs.len; i++) {
931
        unsigned int mask;
932
        if (i < okmasks.len) {
933
            mask = okmasks.intarray[i];
934
        } else {
935
            mask = okmasks.intarray[okmasks.len - 1];
936
        }
937
        LOGDEB2(("checkperms: trying okaddr 0x%x, mask 0x%x\n",
938
                 okaddrs.intarray[i], mask));
939
        if ((ip_addr & mask) == (okaddrs.intarray[i] & mask)) {
940
            return (0);
941
        }
942
    }
943
    LOGERR(("NetconServLis::checkperm: connection from bad address 0x%x\n",
944
            ip_addr));
945
    return -1;
946
}
947
#endif /* NETCON_ACCESSCONTROL */
948
949
950
#else /* !TEST_NETCON */
951
/////////////////////////////////////////////////////////////////////////
952
////////// TEST DRIVER
953
////////////////////////////////////////////////////////////////////////
954
955
#include <stdio.h>
956
#include <stdlib.h>
957
#include <unistd.h>
958
#include <signal.h>
959
#include <string.h>
960
961
#include "debuglog.h"
962
#include "netcon.h"
963
964
using namespace std;
965
966
static char *thisprog;
967
static char usage[] =
968
    "-c <host> <service>: Connects to trnetcon server, exchange message, then\n"
969
    "  sleeps 10 S, except if option -n is given (sleep forever)\n"
970
    "\n"
971
    "-s <service>: open service <service>\n"
972
    ;
973
static void
974
Usage()
975
{
976
    fprintf(stderr, "Usage : %s:\n %s", thisprog, usage);
977
    exit(1);
978
}
979
980
static int     op_flags;
981
#define OPT_MOINS 0x1
982
#define OPT_s     0x2 /* Server */
983
#define OPT_c     0x4 /* Client */
984
#define OPT_n     0x8  /* Client sleeps forever */
985
986
extern int trycli(char *host, char *serv);
987
extern int tryserv(char *serv);
988
989
int nloop = 10;
990
991
int main(int argc, char **argv)
992
{
993
    char *host, *serv;
994
995
    thisprog = argv[0];
996
    argc--;
997
    argv++;
998
999
    while (argc > 0 && **argv == '-') {
1000
        (*argv)++;
1001
        if (!(**argv))
1002
            /* Cas du "adb - core" */
1003
        {
1004
            Usage();
1005
        }
1006
        while (**argv)
1007
            switch (*(*argv)++) {
1008
            case 's':
1009
                op_flags |= OPT_s;
1010
                break;
1011
            case 'c':
1012
                op_flags |= OPT_c;
1013
                break;
1014
            case 'n':
1015
                op_flags |= OPT_n;
1016
                break;
1017
            default:
1018
                Usage();
1019
                break;
1020
            }
1021
        argc--;
1022
        argv++;
1023
    }
1024
    DebugLog::setfilename("stderr");
1025
    DebugLog::getdbl()->setloglevel(DEBDEB2);
1026
1027
    if (op_flags & OPT_c) {
1028
        if (argc != 2) {
1029
            Usage();
1030
        }
1031
        host = *argv++;
1032
        argc--;
1033
        serv = *argv++;
1034
        argc--;
1035
        exit(trycli(host, serv));
1036
    } else if (op_flags & OPT_s) {
1037
        if (argc != 1) {
1038
            Usage();
1039
        }
1040
        serv = *argv++;
1041
        argc--;
1042
        exit(tryserv(serv));
1043
    } else {
1044
        Usage();
1045
    }
1046
}
1047
1048
1049
static char buf[1024];
1050
static int buflen = 1023;
1051
static char fromcli[200];
1052
1053
class CliNetconWorker : public NetconWorker {
1054
public:
1055
    CliNetconWorker()  : m_count(0) {}
1056
    int data(NetconData *con, Netcon::Event reason) {
1057
        LOGDEB(("clientdata\n"));
1058
        if (reason & Netcon::NETCONPOLL_WRITE) {
1059
            sprintf(fromcli, "Bonjour Bonjour client %d, count %d",
1060
                    getpid(), m_count);
1061
            con->setselevents(Netcon::NETCONPOLL_READ);
1062
            if (con->send(fromcli, strlen(fromcli) + 1) < 0) {
1063
                fprintf(stderr, "send failed\n");
1064
                return -1;
1065
            }
1066
            m_count++;
1067
        }
1068
1069
        if (reason & Netcon::NETCONPOLL_READ) {
1070
            con->setselevents(Netcon::NETCONPOLL_WRITE);
1071
            int n;
1072
            if ((n = con->receive(buf, buflen)) < 0) {
1073
                fprintf(stderr, "receive failed\n");
1074
                return -1;
1075
            }
1076
            if (n == 0) {
1077
                // EOF, close connection
1078
                return -1;
1079
            }
1080
            buf[n] = 0;
1081
            fprintf(stderr, "%d received \"%s\"\n", getpid(), buf);
1082
            if (op_flags & OPT_n) {
1083
                pause();
1084
            } else {
1085
                sleep(1);
1086
            }
1087
        }
1088
        if (m_count >= 10) {
1089
            fprintf(stderr, "Did 10, enough\n");
1090
            if (con->getloop()) {
1091
                con->getloop()->loopReturn(0);
1092
            }
1093
        }
1094
        return 0;
1095
    }
1096
private:
1097
    int m_count;
1098
};
1099
1100
int trycli(char *host, char *serv)
1101
{
1102
    sprintf(fromcli, "Bonjour Bonjour je suis le client %d", getpid());
1103
1104
    NetconCli *clicon = new NetconCli();
1105
    NetconP con(clicon);
1106
    if (!con) {
1107
        fprintf(stderr, "new NetconCli failed\n");
1108
        return 1;
1109
    }
1110
    if (clicon->openconn(host, serv) < 0) {
1111
        fprintf(stderr, "openconn(%s, %s) failed\n", host, serv);
1112
        return 1;
1113
    }
1114
    fprintf(stderr, "openconn(%s, %s) ok\n", host, serv);
1115
#ifdef NOSELLOOP
1116
    for (int i = 0; i < nloop; i++) {
1117
        if (con->send(fromcli, strlen(fromcli) + 1) < 0) {
1118
            fprintf(stderr, "%d: Send failed\n", getpid());
1119
            return 1;
1120
        }
1121
        if (con->receive(buf, buflen) < 0) {
1122
            perror("receive:");
1123
            fprintf(stderr, "%d: Receive failed\n", getpid());
1124
            return 1;
1125
        }
1126
        fprintf(stderr, "%d Received \"%s\"\n", getpid(), buf);
1127
        if (op_flags & OPT_n) {
1128
            pause();
1129
        } else {
1130
            sleep(1);
1131
        }
1132
    }
1133
#else
1134
    shared_ptr<CliNetconWorker> worker = make_shared<CliNetconWorker>();
1135
    clicon->setcallback(worker);
1136
    SelectLoop myloop;
1137
    myloop.addselcon(con, Netcon::NETCONPOLL_WRITE);
1138
    fprintf(stderr, "client ready\n");
1139
    int ret = myloop.doLoop();
1140
    if (ret < 0) {
1141
        fprintf(stderr, "selectloop failed\n");
1142
        exit(1);
1143
    }
1144
    fprintf(stderr, "selectloop returned %d\n", ret);
1145
#endif
1146
    return 0;
1147
}
1148
1149
//////////////////////////////////////////////////////////////////
1150
// Server-side sample code
1151
class ServNetconWorker : public NetconWorker {
1152
public:
1153
    ServNetconWorker() : m_count(0) {}
1154
    int data(NetconData *con, Netcon::Event reason) {
1155
        LOGDEB(("serverdata\n"));
1156
        if (reason & Netcon::NETCONPOLL_WRITE) {
1157
            con->setselevents(Netcon::NETCONPOLL_READ);
1158
            char fromserv[200];
1159
            sprintf(fromserv,
1160
                    "Du serveur: mon fd pour ce client est  %d, mon compte %d",
1161
                    con->getfd(), ++m_count);
1162
            if (con->send(fromserv, strlen(fromserv) + 1) < 0) {
1163
                fprintf(stderr, "send failed\n");
1164
                return -1;
1165
            }
1166
        }
1167
        if (reason & Netcon::NETCONPOLL_READ) {
1168
#define LL 200
1169
            char buf[LL + 1];
1170
            int n;
1171
            if ((n = con->receive(buf, LL)) < 0) {
1172
                fprintf(stderr, "receive failed\n");
1173
                return -1;
1174
            }
1175
            if (n == 0) {
1176
                return -1;
1177
            }
1178
            buf[n] = 0;
1179
            fprintf(stderr, "%d received \"%s\"\n", getpid(), buf);
1180
            con->setselevents(Netcon::NETCONPOLL_READ | Netcon::NETCONPOLL_WRITE);
1181
        }
1182
        return 0;
1183
    }
1184
private:
1185
    int m_count;
1186
};
1187
1188
class MyNetconServLis : public NetconServLis {
1189
public:
1190
    MyNetconServLis(SelectLoop& loop)
1191
        : NetconServLis(), m_loop(loop) {
1192
    }
1193
protected:
1194
    int cando(Netcon::Event reason) {
1195
        NetconServCon *con = accept();
1196
        if (con == 0) {
1197
            return -1;
1198
        }
1199
        shared_ptr<ServNetconWorker> worker =
1200
            make_shared<ServNetconWorker>(ServNetconWorker());
1201
        con->setcallback(worker);
1202
        m_loop.addselcon(NetconP(con), NETCONPOLL_READ);
1203
        return 1;
1204
    }
1205
    SelectLoop& m_loop;
1206
};
1207
1208
NetconP lis;
1209
1210
void
1211
onexit(int sig)
1212
{
1213
    fprintf(stderr, "Onexit: ");
1214
    if (sig == SIGQUIT) {
1215
        kill(getpid(), SIGKILL);
1216
    }
1217
    fprintf(stderr, "Exiting\n");
1218
    exit(0);
1219
}
1220
1221
int tryserv(char *serv)
1222
{
1223
    signal(SIGCHLD, SIG_IGN);
1224
    SelectLoop myloop;
1225
    MyNetconServLis *servlis = new MyNetconServLis(myloop);
1226
    lis = NetconP(servlis);
1227
    if (!lis) {
1228
        fprintf(stderr, "new NetconServLis failed\n");
1229
        return 1;
1230
    }
1231
1232
    // Prepare for cleanup
1233
    struct sigaction sa;
1234
    sa.sa_flags = 0;
1235
    sa.sa_handler = onexit;
1236
    sigemptyset(&sa.sa_mask);
1237
    sigaction(SIGINT, &sa, 0);
1238
    sigaction(SIGQUIT, &sa, 0);
1239
    sigaction(SIGTERM, &sa, 0);
1240
1241
    if (servlis->openservice(serv) < 0) {
1242
        fprintf(stderr, "openservice(%s) failed\n", serv);
1243
        return 1;
1244
    }
1245
    myloop.addselcon(lis, Netcon::NETCONPOLL_READ);
1246
    fprintf(stderr, "openservice(%s) Ok\n", serv);
1247
    if (myloop.doLoop() < 0) {
1248
        fprintf(stderr, "selectloop failed\n");
1249
        exit(1);
1250
    }
1251
    return 0;
1252
}
1253
1254
#endif /* TEST_NETCON */