/* * This file is part of DGD, https://github.com/dworkin/dgd * Copyright (C) 1993-2010 Dworkin B.V. * Copyright (C) 2010-2014 DGD Authors (see the commit log for details) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ # include # include # include # include # include # include # include # define INCLUDE_FILE_IO # include "dgd.h" # include "hash.h" # include "comm.h" #ifdef NETWORK_EXTENSIONS #undef INET6 #undef AF_INET6 #endif # ifdef INET6 /* INET6 defined */ # if INET6 == 0 # undef INET6 /* ... but turned off */ # endif # else # ifdef AF_INET6 /* define INET6 if AF_INET6 exists */ # define INET6 # endif # endif # ifndef MAXHOSTNAMELEN # define MAXHOSTNAMELEN 1025 # endif # ifndef INADDR_NONE # define INADDR_NONE 0xffffffffL # endif # define NFREE 32 typedef struct { union { # ifdef INET6 struct in6_addr addr6; /* IPv6 addr */ # endif struct in_addr addr; /* IPv4 addr */ int fd; /* file descriptor */ } in; char ipv6; /* IPv6? */ } in46addr; typedef struct _ipaddr_ { struct _ipaddr_ *link; /* next in hash table */ struct _ipaddr_ *prev; /* previous in linked list */ struct _ipaddr_ *next; /* next in linked list */ Uint ref; /* reference count */ in46addr ipnum; /* ip number */ char name[MAXHOSTNAMELEN]; /* ip name */ } ipaddr; static int in = -1, out = -1; /* pipe to/from name resolver */ static int addrtype; /* network address family */ static ipaddr **ipahtab; /* ip address hash table */ static unsigned int ipahtabsz; /* hash table size */ static ipaddr *qhead, *qtail; /* request queue */ static ipaddr *ffirst, *flast; /* free list */ static int nfree; /* # in free list */ static ipaddr *lastreq; /* last request */ static bool busy; /* name resolver busy */ /* * NAME: ipaddr->run() * DESCRIPTION: host name lookup sub-program */ static void ipa_run(int in, int out) { char buf[sizeof(in46addr)]; struct hostent *host; int len; signal(SIGINT, SIG_IGN); signal(SIGTRAP, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGTSTP, SIG_IGN); while (read(in, buf, sizeof(in46addr)) > 0) { if (((in46addr *) &buf)->ipv6 > 1) { /* close fd copied after hotboot */ close(((in46addr *) &buf)->in.fd); continue; } /* lookup host */ # ifdef INET6 if (((in46addr *) &buf)->ipv6) { host = gethostbyaddr(buf, sizeof(struct in6_addr), AF_INET6); if (host == (struct hostent *) NULL) { sleep(2); host = gethostbyaddr(buf, sizeof(struct in6_addr), AF_INET6); } } else # endif { host = gethostbyaddr(buf, sizeof(struct in_addr), AF_INET); if (host == (struct hostent *) NULL) { sleep(2); host = gethostbyaddr(buf, sizeof(struct in_addr), AF_INET); } } if (host != (struct hostent *) NULL) { char *name; /* write host name */ name = (char *) host->h_name; len = strlen(name); if (len >= MAXHOSTNAMELEN) { len = MAXHOSTNAMELEN - 1; } (void) write(out, name, len); name[0] = '\0'; } else { (void) write(out, "", 1); /* failure */ } } exit(0); /* pipe closed */ } /* * NAME: ipaddr->init() * DESCRIPTION: initialize name lookup */ static bool ipa_init(int maxusers) { if (in < 0) { int fd[4], pid; if (pipe(fd) < 0) { perror("pipe"); return FALSE; } if (pipe(fd + 2) < 0) { perror("pipe"); close(fd[0]); close(fd[1]); return FALSE; } pid = fork(); if (pid < 0) { perror("fork"); close(fd[0]); close(fd[1]); close(fd[2]); close(fd[3]); return FALSE; } if (pid == 0) { /* child process */ close(fd[1]); close(fd[2]); ipa_run(fd[0], fd[3]); } close(fd[0]); close(fd[3]); in = fd[2]; out = fd[1]; } else if (busy) { char buf[MAXHOSTNAMELEN]; /* discard ip name */ (void) read(in, buf, MAXHOSTNAMELEN); } ipahtab = ALLOC(ipaddr*, ipahtabsz = maxusers); memset(ipahtab, '\0', ipahtabsz * sizeof(ipaddr*)); qhead = qtail = ffirst = flast = lastreq = (ipaddr *) NULL; nfree = 0; busy = FALSE; return TRUE; } /* * NAME: ipaddr->finish() * DESCRIPTION: stop name lookup */ static void ipa_finish() { close(out); close(in); } /* * NAME: ipaddr->close() * DESCRIPTION: close a fd duplicated after a hotboot */ static void ipa_close(int fd) { in46addr ipnum; ipnum.ipv6 = 2; ipnum.in.fd = fd; (void) write(out, &ipnum, sizeof(in46addr)); } /* * NAME: ipaddr->new() * DESCRIPTION: return a new ipaddr */ static ipaddr *ipa_new(in46addr *ipnum) { ipaddr *ipa, **hash; /* check hash table */ # ifdef INET6 if (ipnum->ipv6) { hash = &ipahtab[hashmem((char *) ipnum, sizeof(struct in6_addr)) % ipahtabsz]; } else # endif { hash = &ipahtab[(Uint) ipnum->in.addr.s_addr % ipahtabsz]; } while (*hash != (ipaddr *) NULL) { ipa = *hash; # ifdef INET6 if (ipnum->ipv6 == ipa->ipnum.ipv6 && ((ipnum->ipv6) ? memcmp(&ipnum->in.addr6, &ipa->ipnum.in.addr6, sizeof(struct in6_addr)) == 0 : ipnum->in.addr.s_addr == ipa->ipnum.in.addr.s_addr)) { # else if (ipnum->in.addr.s_addr == ipa->ipnum.in.addr.s_addr) { # endif /* * found it */ if (ipa->ref == 0) { /* remove from free list */ if (ipa->prev == (ipaddr *) NULL) { ffirst = ipa->next; } else { ipa->prev->next = ipa->next; } if (ipa->next == (ipaddr *) NULL) { flast = ipa->prev; } else { ipa->next->prev = ipa->prev; } ipa->prev = ipa->next = (ipaddr *) NULL; --nfree; } ipa->ref++; if (ipa->name[0] == '\0' && ipa != lastreq && ipa->prev == (ipaddr *) NULL && ipa != qhead) { if (!busy) { /* send query to name resolver */ (void) write(out, (char *) ipnum, sizeof(in46addr)); lastreq = ipa; busy = TRUE; } else { /* put in request queue */ ipa->prev = qtail; if (qtail == (ipaddr *) NULL) { qhead = ipa; } else { qtail->next = ipa; } qtail = ipa; } } return ipa; } hash = &ipa->link; } if (nfree >= NFREE) { ipaddr **h; /* * use first ipaddr in free list */ ipa = ffirst; ffirst = ipa->next; ffirst->prev = (ipaddr *) NULL; --nfree; if (ipa == lastreq) { lastreq = (ipaddr *) NULL; } if (hash != &ipa->link) { /* remove from hash table */ # ifdef INET6 if (ipa->ipnum.ipv6) { h = &ipahtab[hashmem((char *) &ipa->ipnum, sizeof(struct in6_addr)) % ipahtabsz]; } else # endif { h = &ipahtab[(Uint) ipa->ipnum.in.addr.s_addr % ipahtabsz]; } while (*h != ipa) { h = &(*h)->link; } *h = ipa->link; /* put in hash table */ ipa->link = *hash; *hash = ipa; } } else { /* * allocate new ipaddr */ m_static(); ipa = ALLOC(ipaddr, 1); m_dynamic(); /* put in hash table */ ipa->link = *hash; *hash = ipa; } ipa->ref = 1; ipa->ipnum = *ipnum; ipa->name[0] = '\0'; ipa->prev = ipa->next = (ipaddr *) NULL; if (!busy) { /* send query to name resolver */ (void) write(out, (char *) ipnum, sizeof(in46addr)); lastreq = ipa; busy = TRUE; } else { /* put in request queue */ ipa->prev = qtail; if (qtail == (ipaddr *) NULL) { qhead = ipa; } else { qtail->next = ipa; } qtail = ipa; } return ipa; } /* * NAME: ipaddr->del() * DESCRIPTION: delete an ipaddr */ static void ipa_del(ipaddr *ipa) { if (--ipa->ref == 0) { if (ipa->prev != (ipaddr *) NULL || qhead == ipa) { /* remove from queue */ if (ipa->prev != (ipaddr *) NULL) { ipa->prev->next = ipa->next; } else { qhead = ipa->next; } if (ipa->next != (ipaddr *) NULL) { ipa->next->prev = ipa->prev; } else { qtail = ipa->prev; } } /* add to free list */ if (flast != (ipaddr *) NULL) { flast->next = ipa; ipa->prev = flast; flast = ipa; } else { ffirst = flast = ipa; ipa->prev = (ipaddr *) NULL; } ipa->next = (ipaddr *) NULL; nfree++; } } /* * NAME: ipaddr->lookup() * DESCRIPTION: lookup another ip name */ static void ipa_lookup() { ipaddr *ipa; if (lastreq != (ipaddr *) NULL) { /* read ip name */ lastreq->name[read(in, lastreq->name, MAXHOSTNAMELEN)] = '\0'; } else { char buf[MAXHOSTNAMELEN]; /* discard ip name */ (void) read(in, buf, MAXHOSTNAMELEN); } /* if request queue not empty, write new query */ if (qhead != (ipaddr *) NULL) { ipa = qhead; (void) write(out, (char *) &ipa->ipnum, sizeof(in46addr)); qhead = ipa->next; if (qhead == (ipaddr *) NULL) { qtail = (ipaddr *) NULL; } else { qhead->prev = (ipaddr *) NULL; } ipa->prev = ipa->next = (ipaddr *) NULL; lastreq = ipa; busy = TRUE; } else { lastreq = (ipaddr *) NULL; busy = FALSE; } } struct _connection_ { hte chain; /* UDP challenge hash chain */ int fd; /* file descriptor */ int npkts; /* # packets in buffer */ int bufsz; /* # bytes in buffer */ char *udpbuf; /* datagram buffer */ ipaddr *addr; /* internet address of connection */ unsigned short port; /* UDP port of connection */ short at; /* port connection was accepted at */ struct _connection_ *next; /* next in list */ }; typedef struct { int in6; /* IPv6 port descriptor */ int in4; /* IPv4 port descriptor */ } portdesc; static int nusers; /* # of users */ static connection *connections; /* connections array */ static connection *flist; /* list of free connections */ static connection **udphtab; /* UDP hash table */ static int udphtabsz; /* UDP hash table size */ static hashtab *chtab; /* challenge hash table */ static portdesc *tdescs, *bdescs; /* telnet & binary descriptor arrays */ static int ntdescs, nbdescs; /* # telnet & binary ports */ static portdesc *udescs; /* UDP port descriptor array */ static fd_set infds; /* file descriptor input bitmap */ static fd_set outfds; /* file descriptor output bitmap */ static fd_set waitfds; /* file descriptor wait-write bitmap */ static fd_set readfds; /* file descriptor read bitmap */ static fd_set writefds; /* file descriptor write map */ static int maxfd; /* largest fd opened yet */ static int npackets; /* # packets buffered */ static int closed; /* #fds closed in write */ # ifdef INET6 /* * NAME: conn->port6() * DESCRIPTION: open an IPv6 port */ static int conn_port6(int *fd, int type, struct sockaddr_in6 *sin6, unsigned int port) { int on; if ((*fd=socket(AF_INET6, type, 0)) < 0) { perror("socket IPv6"); return FALSE; } if (*fd > maxfd) { maxfd = *fd; } on = 1; if (setsockopt(*fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { perror("setsockopt"); return FALSE; } # if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) on = 1; if (setsockopt(*fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { perror("setsockopt"); return FALSE; } # endif # ifdef SO_OOBINLINE if (type == SOCK_STREAM) { on = 1; if (setsockopt(*fd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0) { perror("setsockopt"); return FALSE; } } # endif sin6->sin6_port = htons(port); if (bind(*fd, (struct sockaddr *) sin6, sizeof(struct sockaddr_in6)) < 0) { perror("bind"); return FALSE; } FD_SET(*fd, &infds); return TRUE; } # endif /* * NAME: conn->port() * DESCRIPTION: open an IPv4 port */ static int conn_port(int *fd, int type, struct sockaddr_in *sin, unsigned int port) { int on; if ((*fd=socket(AF_INET, type, 0)) < 0) { perror("socket"); return FALSE; } if (*fd > maxfd) { maxfd = *fd; } on = 1; if (setsockopt(*fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { perror("setsockopt"); return FALSE; } # ifdef SO_OOBINLINE if (type == SOCK_STREAM) { on = 1; if (setsockopt(*fd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0) { perror("setsockopt"); return FALSE; } } # endif sin->sin_port = htons(port); if (bind(*fd, (struct sockaddr *) sin, sizeof(struct sockaddr_in)) < 0) { perror("bind"); return FALSE; } FD_SET(*fd, &infds); return TRUE; } /* * NAME: conn->init() * DESCRIPTION: initialize connection handling */ bool conn_init(int maxusers, char **thosts, char **bhosts, unsigned short *tports, unsigned short *bports, int ntports, int nbports) { # ifdef INET6 struct sockaddr_in6 sin6; # endif struct sockaddr_in sin; struct hostent *host; int n; connection *conn; bool ipv6, ipv4; # ifdef AI_DEFAULT int err; # endif if (!ipa_init(maxusers)) { return FALSE; } addrtype = PF_INET; nusers = 0; maxfd = 0; FD_ZERO(&infds); FD_ZERO(&outfds); FD_ZERO(&waitfds); FD_SET(in, &infds); npackets = 0; closed = 0; #ifndef NETWORK_EXTENSIONS ntdescs = ntports; if (ntports != 0) { tdescs = ALLOC(portdesc, ntports); memset(tdescs, -1, ntports * sizeof(portdesc)); } nbdescs = nbports; if (nbports != 0) { bdescs = ALLOC(portdesc, nbports); memset(bdescs, -1, nbports * sizeof(portdesc)); udescs = ALLOC(portdesc, nbports); memset(udescs, -1, nbports * sizeof(portdesc)); } #endif # ifdef INET6 memset(&sin6, '\0', sizeof(sin6)); sin6.sin6_family = AF_INET6; # endif memset(&sin, '\0', sizeof(sin)); sin.sin_family = AF_INET; for (n = 0; n < ntdescs; n++) { /* telnet ports */ ipv6 = FALSE; ipv4 = FALSE; if (thosts[n] == (char *) NULL) { # ifdef INET6 sin6.sin6_addr = in6addr_any; ipv6 = TRUE; # endif sin.sin_addr.s_addr = INADDR_ANY; ipv4 = TRUE; } else { # ifdef INET6 if (inet_pton(AF_INET6, thosts[n], &sin6) > 0) { sin6.sin6_family = AF_INET6; ipv6 = TRUE; } else { # ifdef AI_DEFAULT host = getipnodebyname(thosts[n], AF_INET6, 0, &err); if (host != (struct hostent *) NULL) { if (host->h_length != 4) { memcpy(&sin6.sin6_addr, host->h_addr, host->h_length); ipv6 = TRUE; } freehostent(host); } # else host = gethostbyname2(thosts[n], AF_INET6); if (host != (struct hostent *) NULL && host->h_length != 4) { memcpy(&sin6.sin6_addr, host->h_addr, host->h_length); ipv6 = TRUE; } # endif } # endif if ((sin.sin_addr.s_addr=inet_addr(thosts[n])) != INADDR_NONE) { ipv4 = TRUE; } else { host = gethostbyname(thosts[n]); if (host != (struct hostent *) NULL) { memcpy(&sin.sin_addr, host->h_addr, host->h_length); ipv4 = TRUE; } } } if (!ipv6 && !ipv4) { message("unknown host %s\012", thosts[n]); /* LF */ return FALSE; } # ifdef INET6 if (ipv6 && !conn_port6(&tdescs[n].in6, SOCK_STREAM, &sin6, tports[n])) { return FALSE; } # endif if (ipv4 && !conn_port(&tdescs[n].in4, SOCK_STREAM, &sin, tports[n])) { return FALSE; } } for (n = 0; n < nbdescs; n++) { /* binary ports */ ipv6 = FALSE; ipv4 = FALSE; if (bhosts[n] == (char *) NULL) { # ifdef INET6 sin6.sin6_addr = in6addr_any; ipv6 = TRUE; # endif sin.sin_addr.s_addr = INADDR_ANY; ipv4 = TRUE; } else { # ifdef INET6 if (inet_pton(AF_INET6, bhosts[n], &sin6) > 0) { sin6.sin6_family = AF_INET6; ipv6 = TRUE; } else { # ifdef AI_DEFAULT host = getipnodebyname(bhosts[n], AF_INET6, 0, &err); if (host != (struct hostent *) NULL) { if (host->h_length != 4) { memcpy(&sin6.sin6_addr, host->h_addr, host->h_length); ipv6 = TRUE; } freehostent(host); } # else host = gethostbyname2(bhosts[n], AF_INET6); if (host != (struct hostent *) NULL && host->h_length != 4) { memcpy(&sin6.sin6_addr, host->h_addr, host->h_length); ipv6 = TRUE; } # endif } # endif if ((sin.sin_addr.s_addr=inet_addr(bhosts[n])) != INADDR_NONE) { ipv4 = TRUE; } else { host = gethostbyname(bhosts[n]); if (host != (struct hostent *) NULL) { memcpy(&sin.sin_addr, host->h_addr, host->h_length); ipv4 = TRUE; } } } if (!ipv6 && !ipv4) { message("unknown host %s\012", bhosts[n]); /* LF */ return FALSE; } # ifdef INET6 if (ipv6) { if (!conn_port6(&bdescs[n].in6, SOCK_STREAM, &sin6, bports[n])) { return FALSE; } if (!conn_port6(&udescs[n].in6, SOCK_DGRAM, &sin6, bports[n])) { return FALSE; } } # endif if (ipv4) { if (!conn_port(&bdescs[n].in4, SOCK_STREAM, &sin, bports[n])) { return FALSE; } if (!conn_port(&udescs[n].in4, SOCK_DGRAM, &sin, bports[n])) { return FALSE; } } } flist = (connection *) NULL; #ifndef NETWORK_EXTENSIONS connections = ALLOC(connection, nusers = maxusers); #else connections = ALLOC(connection, nusers = maxusers+1); #endif for (n = nusers, conn = connections; n > 0; --n, conn++) { conn->fd = -1; conn->chain.next = (hte *) flist; flist = conn; } #ifndef NETWORK_EXTENSIONS udphtab = ALLOC(connection*, udphtabsz = maxusers); memset(udphtab, '\0', udphtabsz * sizeof(connection*)); chtab = ht_new(maxusers, UDPHASHSZ, TRUE); #endif return TRUE; } /* * NAME: conn->clear() * DESCRIPTION: clean up connections */ void conn_clear() { int n; for (n = 0; n < ntdescs; n++) { if (tdescs[n].in6 >= 0) { close(tdescs[n].in6); } if (tdescs[n].in4 >= 0) { close(tdescs[n].in4); } } for (n = 0; n < nbdescs; n++) { if (bdescs[n].in6 >= 0) { close(bdescs[n].in6); } if (bdescs[n].in4 >= 0) { close(bdescs[n].in4); } if (udescs[n].in6 >= 0) { close(udescs[n].in6); } if (udescs[n].in4 >= 0) { close(udescs[n].in4); } } ipa_finish(); } /* * NAME: conn->finish() * DESCRIPTION: terminate connections */ void conn_finish() { int n; connection *conn; for (n = nusers, conn = connections; n > 0; --n, conn++) { if (conn->fd >= 0) { close(conn->fd); } } } #ifndef NETWORK_EXTENSIONS /* * NAME: conn->listen() * DESCRIPTION: start listening on telnet port and binary port */ void conn_listen() { int n; for (n = 0; n < ntdescs; n++) { if (tdescs[n].in6 >= 0) { if (listen(tdescs[n].in6, 64) < 0) { perror("listen"); } else if (fcntl(tdescs[n].in6, F_SETFL, FNDELAY) < 0) { perror("fcntl"); } else { continue; } fatal("conn_listen failed"); } } for (n = 0; n < ntdescs; n++) { if (tdescs[n].in4 >= 0) { if (listen(tdescs[n].in4, 64) < 0) { # ifdef INET6 close(tdescs[n].in4); FD_CLR(tdescs[n].in4, &infds); tdescs[n].in4 = -1; continue; # else perror("listen"); # endif } else if (fcntl(tdescs[n].in4, F_SETFL, FNDELAY) < 0) { perror("fcntl"); } else { continue; } fatal("conn_listen failed"); } } for (n = 0; n < nbdescs; n++) { if (bdescs[n].in6 >= 0) { if (listen(bdescs[n].in6, 64) < 0) { perror("listen"); } else if (fcntl(bdescs[n].in6, F_SETFL, FNDELAY) < 0) { perror("fcntl"); } else if (fcntl(udescs[n].in6, F_SETFL, FNDELAY) < 0) { perror("fcntl"); } else { continue; } fatal("conn_listen failed"); } } for (n = 0; n < nbdescs; n++) { if (bdescs[n].in4 >= 0) { if (listen(bdescs[n].in4, 64) < 0) { # ifdef INET6 close(bdescs[n].in4); FD_CLR(bdescs[n].in4, &infds); bdescs[n].in4 = -1; continue; # else perror("listen"); # endif } else if (fcntl(bdescs[n].in4, F_SETFL, FNDELAY) < 0) { perror("fcntl"); } else if (fcntl(udescs[n].in4, F_SETFL, FNDELAY) < 0) { perror("fcntl"); } else { continue; } fatal("conn_listen failed"); } } } # ifdef INET6 /* * NAME: conn->accept6() * DESCRIPTION: accept a new ipv6 connection */ static connection *conn_accept6(int portfd, int port) { int fd; unsigned int len; struct sockaddr_in6 sin6; in46addr addr; connection *conn; if (!FD_ISSET(portfd, &readfds)) { return (connection *) NULL; } len = sizeof(sin6); fd = accept(portfd, (struct sockaddr *) &sin6, &len); if (fd < 0) { FD_CLR(portfd, &readfds); return (connection *) NULL; } fcntl(fd, F_SETFL, FNDELAY); conn = flist; flist = (connection *) conn->chain.next; conn->chain.name = (char *) NULL; conn->fd = fd; conn->udpbuf = (char *) NULL; if (IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) { /* convert to IPv4 address */ addr.in.addr = *(struct in_addr *) &sin6.sin6_addr.s6_addr[12]; addr.ipv6 = FALSE; } else { addr.in.addr6 = sin6.sin6_addr; addr.ipv6 = TRUE; } conn->addr = ipa_new(&addr); conn->at = port; FD_SET(fd, &infds); FD_SET(fd, &outfds); FD_CLR(fd, &readfds); FD_SET(fd, &writefds); if (fd > maxfd) { maxfd = fd; } return conn; } # endif /* * NAME: conn->accept() * DESCRIPTION: accept a new ipv4 connection */ static connection *conn_accept(int portfd, int port) { int fd; unsigned int len; struct sockaddr_in sin; in46addr addr; connection *conn; if (!FD_ISSET(portfd, &readfds)) { return (connection *) NULL; } len = sizeof(sin); fd = accept(portfd, (struct sockaddr *) &sin, &len); if (fd < 0) { FD_CLR(portfd, &readfds); return (connection *) NULL; } fcntl(fd, F_SETFL, FNDELAY); conn = flist; flist = (connection *) conn->chain.next; conn->chain.name = (char *) NULL; conn->fd = fd; conn->udpbuf = (char *) NULL; addr.in.addr = sin.sin_addr; addr.ipv6 = FALSE; conn->addr = ipa_new(&addr); conn->at = port; FD_SET(fd, &infds); FD_SET(fd, &outfds); FD_CLR(fd, &readfds); FD_SET(fd, &writefds); if (fd > maxfd) { maxfd = fd; } return conn; } /* * NAME: conn->tnew6() * DESCRIPTION: accept a new telnet connection */ connection *conn_tnew6(int port) { # ifdef INET6 int fd; fd = tdescs[port].in6; if (fd >= 0) { return conn_accept6(fd, port); } # endif return (connection *) NULL; } /* * NAME: conn->bnew6() * DESCRIPTION: accept a new binary connection */ connection *conn_bnew6(int port) { # ifdef INET6 int fd; fd = bdescs[port].in6; if (fd >= 0) { return conn_accept6(fd, port); } # endif return (connection *) NULL; } /* * NAME: conn->tnew() * DESCRIPTION: accept a new telnet connection */ connection *conn_tnew(int port) { int fd; fd = tdescs[port].in4; if (fd >= 0) { return conn_accept(fd, port); } return (connection *) NULL; } /* * NAME: conn->bnew() * DESCRIPTION: accept a new binary connection */ connection *conn_bnew(int port) { int fd; fd = bdescs[port].in4; if (fd >= 0) { return conn_accept(fd, port); } return (connection *) NULL; } /* * NAME: conn->udp() * DESCRIPTION: set the challenge for attaching a UDP channel */ bool conn_udp(connection *conn, char *challenge, unsigned int len) { char buffer[UDPHASHSZ]; connection **hash; if (len == 0 || len > BINBUF_SIZE || conn->udpbuf != (char *) NULL) { return FALSE; /* invalid challenge */ } if (len >= UDPHASHSZ) { memcpy(buffer, challenge, UDPHASHSZ); } else { memset(buffer, '\0', UDPHASHSZ); memcpy(buffer, challenge, len); } hash = (connection **) ht_lookup(chtab, buffer, FALSE); while (*hash != (connection *) NULL && memcmp((*hash)->chain.name, buffer, UDPHASHSZ) == 0) { if ((*hash)->bufsz == len && memcmp((*hash)->udpbuf, challenge, len) == 0) { return FALSE; /* duplicate challenge */ } } conn->chain.next = (hte *) *hash; *hash = conn; conn->npkts = 0; m_static(); conn->udpbuf = ALLOC(char, BINBUF_SIZE); m_dynamic(); memset(conn->udpbuf, '\0', UDPHASHSZ); memcpy(conn->chain.name = conn->udpbuf, challenge, conn->bufsz = len); return TRUE; } #endif /* * NAME: conn->del() * DESCRIPTION: delete a connection */ void conn_del(connection *conn) { connection **hash; if (conn->fd >= 0) { close(conn->fd); FD_CLR(conn->fd, &infds); FD_CLR(conn->fd, &outfds); FD_CLR(conn->fd, &waitfds); conn->fd = -1; } else { --closed; } if (conn->udpbuf != (char *) NULL) { if (conn->chain.name != (char *) NULL) { hash = (connection **) ht_lookup(chtab, conn->chain.name, FALSE); # ifdef INET6 } else if (conn->addr->ipnum.ipv6) { hash = &udphtab[(hashmem((char *) &conn->addr->ipnum, sizeof(struct in6_addr)) ^ conn->port) % udphtabsz]; # endif } else { hash = &udphtab[(((Uint) conn->addr->ipnum.in.addr.s_addr) ^ conn->port) % udphtabsz]; } while (*hash != conn) { hash = (connection **) &(*hash)->chain.next; } *hash = (connection *) conn->chain.next; npackets -= conn->npkts; FREE(conn->udpbuf); } if (conn->addr != (ipaddr *) NULL) { ipa_del(conn->addr); } conn->chain.next = (hte *) flist; flist = conn; } /* * NAME: conn->block() * DESCRIPTION: block or unblock input from connection */ void conn_block(connection *conn, int flag) { if (conn->fd >= 0) { if (flag) { FD_CLR(conn->fd, &infds); FD_CLR(conn->fd, &readfds); } else { FD_SET(conn->fd, &infds); } } } # ifdef INET6 /* * NAME: conn->udprecv6() * DESCRIPTION: receive an UDP packet */ static void conn_udprecv6(int n) { char buffer[BINBUF_SIZE]; struct sockaddr_in6 from; unsigned int fromlen; int size; connection **hash, *conn; char *p; memset(buffer, '\0', UDPHASHSZ); fromlen = sizeof(struct sockaddr_in6); size = recvfrom(udescs[n].in6, buffer, BINBUF_SIZE, 0, (struct sockaddr *) &from, &fromlen); if (size < 0) { return; } hash = &udphtab[(hashmem((char *) &from.sin6_addr, sizeof(struct in6_addr)) ^ from.sin6_port) % udphtabsz]; for (;;) { conn = *hash; if (conn == (connection *) NULL) { /* * see if the packet matches an outstanding challenge */ hash = (connection **) ht_lookup(chtab, buffer, FALSE); while ((conn=*hash) != (connection *) NULL && memcmp(conn->chain.name, buffer, UDPHASHSZ) == 0) { if (conn->bufsz == size && memcmp(conn->udpbuf, buffer, size) == 0 && conn->addr->ipnum.ipv6 && memcmp(&conn->addr->ipnum, &from.sin6_addr, sizeof(struct in6_addr)) == 0) { /* * attach new UDP channel */ *hash = (connection *) conn->chain.next; conn->chain.name = (char *) NULL; conn->bufsz = 0; conn->port = from.sin6_port; hash = &udphtab[(hashmem((char *) &from.sin6_addr, sizeof(struct in6_addr)) ^ conn->port) % udphtabsz]; conn->chain.next = (hte *) *hash; *hash = conn; break; } hash = (connection **) &conn->chain.next; } break; } if (conn->at == n && conn->port == from.sin6_port && memcmp(&conn->addr->ipnum, &from.sin6_addr, sizeof(struct in6_addr)) == 0) { /* * packet from known correspondent */ if (conn->bufsz + size <= BINBUF_SIZE - 2) { p = conn->udpbuf + conn->bufsz; *p++ = size >> 8; *p++ = size; memcpy(p, buffer, size); conn->bufsz += size + 2; conn->npkts++; npackets++; } break; } hash = (connection **) &conn->chain.next; } } # endif /* * NAME: conn->udprecv() * DESCRIPTION: receive an UDP packet */ static void conn_udprecv(int n) { char buffer[BINBUF_SIZE]; struct sockaddr_in from; unsigned int fromlen; int size; connection **hash, *conn; char *p; memset(buffer, '\0', UDPHASHSZ); fromlen = sizeof(struct sockaddr_in); size = recvfrom(udescs[n].in4, buffer, BINBUF_SIZE, 0, (struct sockaddr *) &from, &fromlen); if (size < 0) { return; } hash = &udphtab[((Uint) from.sin_addr.s_addr ^ from.sin_port) % udphtabsz]; for (;;) { conn = *hash; if (conn == (connection *) NULL) { /* * see if the packet matches an outstanding challenge */ hash = (connection **) ht_lookup(chtab, buffer, FALSE); while ((conn=*hash) != (connection *) NULL && memcmp((*hash)->chain.name, buffer, UDPHASHSZ) == 0) { if (conn->bufsz == size && memcmp(conn->udpbuf, buffer, size) == 0 && !conn->addr->ipnum.ipv6 && conn->addr->ipnum.in.addr.s_addr == from.sin_addr.s_addr) { /* * attach new UDP channel */ *hash = (connection *) conn->chain.next; conn->chain.name = (char *) NULL; conn->bufsz = 0; conn->port = from.sin_port; hash = &udphtab[((Uint) from.sin_addr.s_addr ^ conn->port) % udphtabsz]; conn->chain.next = (hte *) *hash; *hash = conn; break; } hash = (connection **) &conn->chain.next; } break; } if (conn->at == n && conn->addr->ipnum.in.addr.s_addr == from.sin_addr.s_addr && conn->port == from.sin_port) { /* * packet from known correspondent */ if (conn->bufsz + size <= BINBUF_SIZE - 2) { p = conn->udpbuf + conn->bufsz; *p++ = size >> 8; *p++ = size; memcpy(p, buffer, size); conn->bufsz += size + 2; conn->npkts++; npackets++; } break; } hash = (connection **) &conn->chain.next; } } /* * NAME: conn->select() * DESCRIPTION: wait for input from connections */ int conn_select(Uint t, unsigned int mtime) { struct timeval timeout; int retval; int n; /* * First, check readability and writability for binary sockets with pending * data only. */ memcpy(&readfds, &infds, sizeof(fd_set)); if (flist == (connection *) NULL) { /* can't accept new connections, so don't check for them */ for (n = ntdescs; n != 0; ) { --n; if (tdescs[n].in6 >= 0) { FD_CLR(tdescs[n].in6, &readfds); } if (tdescs[n].in4 >= 0) { FD_CLR(tdescs[n].in4, &readfds); } } for (n = nbdescs; n != 0; ) { --n; if (bdescs[n].in6 >= 0) { FD_CLR(bdescs[n].in6, &readfds); } if (bdescs[n].in4 >= 0) { FD_CLR(bdescs[n].in4, &readfds); } } } memcpy(&writefds, &waitfds, sizeof(fd_set)); if (npackets + closed != 0) { t = 0; mtime = 0; } if (mtime != 0xffff) { timeout.tv_sec = t; timeout.tv_usec = mtime * 1000L; retval = select(maxfd + 1, &readfds, &writefds, (fd_set *) NULL, &timeout); } else { retval = select(maxfd + 1, &readfds, &writefds, (fd_set *) NULL, (struct timeval *) NULL); } if (retval < 0) { FD_ZERO(&readfds); retval = 0; } /* check for UDP packets */ for (n = 0; n < nbdescs; n++) { # ifdef INET6 if (udescs[n].in6 >= 0 && FD_ISSET(udescs[n].in6, &readfds)) { conn_udprecv6(n); } # endif if (udescs[n].in4 >= 0 && FD_ISSET(udescs[n].in4, &readfds)) { conn_udprecv(n); } } retval += npackets + closed; /* * Now check writability for all sockets in a polling call. */ memcpy(&writefds, &outfds, sizeof(fd_set)); timeout.tv_sec = 0; timeout.tv_usec = 0; select(maxfd + 1, (fd_set *) NULL, &writefds, (fd_set *) NULL, &timeout); /* handle ip name lookup */ if (FD_ISSET(in, &readfds)) { ipa_lookup(); } return retval; } #ifndef NETWORK_EXTENSIONS /* * NAME: conn->udpcheck() * DESCRIPTION: check if UDP challenge met */ bool conn_udpcheck(connection *conn) { return (conn->chain.name == (char *) NULL); } #endif /* * NAME: conn->read() * DESCRIPTION: read from a connection */ int conn_read(connection *conn, char *buf, unsigned int len) { int size; if (conn->fd < 0) { return -1; } if (!FD_ISSET(conn->fd, &readfds)) { return 0; } size = read(conn->fd, buf, len); if (size < 0) { close(conn->fd); FD_CLR(conn->fd, &infds); FD_CLR(conn->fd, &outfds); FD_CLR(conn->fd, &waitfds); conn->fd = -1; closed++; } return (size == 0) ? -1 : size; } /* * NAME: conn->udpread() * DESCRIPTION: read a message from a UDP channel */ int conn_udpread(connection *conn, char *buf, unsigned int len) { unsigned short size, n; char *p, *q; while (conn->bufsz != 0) { /* udp buffer is not empty */ size = (UCHAR(conn->udpbuf[0]) << 8) | UCHAR(conn->udpbuf[1]); if (size <= len) { memcpy(buf, conn->udpbuf + 2, len = size); } --conn->npkts; --npackets; conn->bufsz -= size + 2; for (p = conn->udpbuf, q = p + size + 2, n = conn->bufsz; n != 0; --n) { *p++ = *q++; } if (len == size) { return len; } } return -1; } /* * NAME: conn->write() * DESCRIPTION: write to a connection; return the amount of bytes written */ int conn_write(connection *conn, char *buf, unsigned int len) { int size; if (conn->fd < 0) { return -1; } if (len == 0) { return 0; } if (!FD_ISSET(conn->fd, &writefds)) { /* the write would fail */ FD_SET(conn->fd, &waitfds); return 0; } if ((size=write(conn->fd, buf, len)) < 0 && errno != EWOULDBLOCK) { close(conn->fd); FD_CLR(conn->fd, &infds); FD_CLR(conn->fd, &outfds); conn->fd = -1; closed++; } else if (size != len) { /* waiting for wrdone */ FD_SET(conn->fd, &waitfds); FD_CLR(conn->fd, &writefds); if (size < 0) { return 0; } } return size; } /* * NAME: conn->udpwrite() * DESCRIPTION: write a message to a UDP channel */ int conn_udpwrite(connection *conn, char *buf, unsigned int len) { if (conn->fd >= 0) { # ifdef INET6 if (conn->addr->ipnum.ipv6) { struct sockaddr_in6 to; memset(&to, '\0', sizeof(struct sockaddr_in6)); to.sin6_family = AF_INET6; memcpy(&to.sin6_addr, &conn->addr->ipnum.in.addr6, sizeof(struct in6_addr)); to.sin6_port = conn->port; return sendto(udescs[conn->at].in6, buf, len, 0, (struct sockaddr *) &to, sizeof(struct sockaddr_in6)); } else # endif { struct sockaddr_in to; memset(&to, '\0', sizeof(struct sockaddr_in)); to.sin_family = AF_INET; to.sin_addr = conn->addr->ipnum.in.addr; to.sin_port = conn->port; return sendto(udescs[conn->at].in4, buf, len, 0, (struct sockaddr *) &to, sizeof(struct sockaddr_in)); } } return -1; } #ifdef NETWORK_EXTENSIONS int conn_udpsend(connection *conn, char *buf, unsigned int len, char *addr, unsigned short port) { struct sockaddr_in to; to.sin_family=addrtype; inet_aton(addr, &to.sin_addr); /* should have been checked for valid addresses already, so it should not fail */ to.sin_port = htons(port); if (!sendto(conn->fd, buf, len, 0, (struct sockaddr *) &to, sizeof(struct sockaddr_in))) { if (errno==EAGAIN) { return -1; } perror("sendto"); return -2; } return 0; } /* * NAME: conn->checkaddr() * DESCRIPTION: checks for valid ip address */ int conn_checkaddr(char *ip) { struct in_addr dummy; return inet_aton(ip, &dummy); } #endif /* * NAME: conn->wrdone() * DESCRIPTION: return TRUE if a connection is ready for output */ bool conn_wrdone(connection *conn) { if (conn->fd < 0 || !FD_ISSET(conn->fd, &waitfds)) { return TRUE; } if (FD_ISSET(conn->fd, &writefds)) { FD_CLR(conn->fd, &waitfds); return TRUE; } return FALSE; } /* * NAME: conn->ipnum() * DESCRIPTION: return the ip number of a connection */ void conn_ipnum(connection *conn, char *buf) { # ifdef INET6 /* IPv6: maxlen 39 */ if (conn->addr->ipnum.ipv6) { inet_ntop(AF_INET6, &conn->addr->ipnum, buf, 40); } else # endif { strcpy(buf, inet_ntoa(conn->addr->ipnum.in.addr)); } } /* * NAME: conn->ipname() * DESCRIPTION: return the ip name of a connection */ void conn_ipname(connection *conn, char *buf) { if (conn->addr->name[0] != '\0') { strcpy(buf, conn->addr->name); } else { conn_ipnum(conn, buf); } } /* * NAME: conn->host() * DESCRIPTION: look up a host */ void *conn_host(char *addr, unsigned short port, int *len) { struct hostent *host; static union { struct sockaddr_in sin; # ifdef INET6 struct sockaddr_in6 sin6; # endif } inaddr; memset(&inaddr.sin, '\0', sizeof(struct sockaddr_in)); inaddr.sin.sin_family = AF_INET; inaddr.sin.sin_port = htons(port); *len = sizeof(struct sockaddr_in); if ((inaddr.sin.sin_addr.s_addr=inet_addr(addr)) != INADDR_NONE) { return &inaddr; } else { host = gethostbyname(addr); if (host != (struct hostent *) NULL) { memcpy(&inaddr.sin.sin_addr, host->h_addr, host->h_length); return &inaddr; } } # ifdef INET6 memset(&inaddr.sin6, '\0', sizeof(struct sockaddr_in6)); inaddr.sin6.sin6_family = AF_INET6; *len = sizeof(struct sockaddr_in6); if (inet_pton(AF_INET6, addr, &inaddr.sin6) > 0) { inaddr.sin6.sin6_family = AF_INET6; inaddr.sin6.sin6_port = htons(port); return &inaddr; } else { # ifdef AI_DEFAULT int err; host = getipnodebyname(addr, AF_INET6, 0, &err); # else host = gethostbyname2(addr, AF_INET6); # endif if (host != (struct hostent *) NULL) { if (host->h_length != 4) { memcpy(&inaddr.sin6.sin6_addr, host->h_addr, host->h_length); # ifdef AI_DEFAULT freehostent(host); # endif inaddr.sin6.sin6_port = htons(port); return &inaddr; } # ifdef AI_DEFAULT freehostent(host); # endif } } # endif return (void *) NULL; } /* * NAME: conn->connect() * DESCRIPTION: establish an oubound connection */ connection *conn_connect(void *addr, int len) { connection *conn; int sock; int on; long arg; if (flist == (connection *) NULL) { return NULL; } sock = socket(((struct sockaddr_in *) addr)->sin_family, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); return NULL; } on = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) { perror("setsockopt"); close(sock); return NULL; } #ifdef SO_OOBINLINE on = 1; if (setsockopt(sock, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0) { perror("setsockopt"); close(sock); return NULL; } #endif if ((arg = fcntl(sock, F_GETFL, NULL)) < 0) { perror("fcntl"); close(sock); return NULL; } arg |= O_NONBLOCK; if (fcntl(sock, F_SETFL, arg) < 0) { perror("fcntl"); close(sock); return NULL; } connect(sock, (struct sockaddr *) addr, len); conn = flist; flist = (connection *) conn->chain.next; conn->fd = sock; conn->chain.name = (char *) NULL; conn->udpbuf = (char *) NULL; conn->addr = (ipaddr *) NULL; conn->at = -1; FD_SET(sock, &infds); FD_SET(sock, &outfds); FD_CLR(sock, &readfds); FD_CLR(sock, &writefds); FD_SET(sock, &waitfds); if (sock > maxfd) { maxfd = sock; } return conn; } /* * check for a connection in pending state and see if it is connected. */ int conn_check_connected(connection *conn, bool *refused) { int optval; socklen_t lon; /* * indicate that our fd became invalid. */ if (conn->fd < 0) { return -2; } if (!FD_ISSET(conn->fd, &writefds)) { return 0; } FD_CLR(conn->fd, &waitfds); /* * Delayed connect completed, check for errors */ lon = sizeof(int); /* * Get error state for the socket */ *refused = FALSE; if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, (void *)(&optval), &lon) < 0) { return -1; } if (optval != 0) { if (optval == ECONNREFUSED) { *refused = TRUE; } errno = optval; return -1; } else { # ifdef INET6 struct sockaddr_in6 sin; # else struct sockaddr_in sin; # endif socklen_t len; in46addr inaddr; len = sizeof(sin); getpeername(conn->fd, (struct sockaddr *) &sin, &len); inaddr.ipv6 = FALSE; # ifdef INET6 if (sin.sin6_family == AF_INET6) { inaddr.in.addr6 = sin.sin6_addr; inaddr.ipv6 = TRUE; } else # endif inaddr.in.addr = ((struct sockaddr_in *) &sin)->sin_addr; conn->addr = ipa_new(&inaddr); errno = 0; return 1; } } #ifdef NETWORK_EXTENSIONS /* * Name: conn->openlisten() * DESCRIPTION: open a new listening connection */ connection *conn_openlisten(unsigned char protocol, unsigned short port) { struct sockaddr_in sin; connection *conn; int on, n, sock; unsigned int sz; switch (protocol) { case P_TCP: sock = socket(addrtype, SOCK_STREAM, 0); if (sock < 0){ perror("socket"); return NULL; } on = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0){ perror("setsockopt"); close(sock); return NULL; } # ifdef SO_OOBINLINE on = 1; if (setsockopt(sock, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(on)) < 0) { perror("setsockopt"); close(sock); return NULL; } # endif memset(&sin, '\0', sizeof(sin)); sin.sin_port = htons(port); sin.sin_family = addrtype; sin.sin_addr.s_addr = INADDR_ANY; if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { perror("bind"); close(sock); return NULL; } if (listen(sock,64)) { perror("listen"); close(sock); return NULL; } FD_SET(sock, &infds); if (maxfd < sock) { maxfd = sock; } conn = flist; flist = (connection *) conn->chain.next; conn->fd = sock; conn->chain.name = (char *) NULL; conn->udpbuf = (char *) NULL; conn->addr = (ipaddr *) NULL; sz = sizeof(sin); getsockname(conn->fd, (struct sockaddr *) &sin, &sz); conn->port = ntohs(sin.sin_port); conn->at = -1; return conn; case P_UDP: sock = socket(addrtype, SOCK_DGRAM, 0); if (sock < 0) { perror("socket"); return NULL; } on = 0; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) { perror("setsockopt"); close(sock); return NULL; } memset(&sin, '\0', sizeof(sin)); sin.sin_port = htons(port); sin.sin_family = addrtype; sin.sin_addr.s_addr = INADDR_ANY; if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { perror("bind"); close(sock); return NULL; } if (fcntl(sock, F_SETFL, FNDELAY)) { perror("fcntl"); close(sock); return NULL; } FD_SET(sock, &infds); if (maxfd < sock) { maxfd = sock; } conn = flist; flist = (connection *) conn->chain.next; conn->fd = sock; conn->chain.name = (char *) NULL; conn->udpbuf = (char *) NULL; conn->addr = (ipaddr *) NULL; sz = sizeof(sin); getsockname(conn->fd, (struct sockaddr *) &sin, &sz); conn->port = ntohs(sin.sin_port); conn->at = -1; return conn; default: return NULL; } } /* * NAME: conn->port() * DESCRIPTION: return the port number of a connection */ int conn_at(connection *conn) { return conn->port; } /* * NAME: conn->accept() * DESCRIPTION: return a new connction structure */ connection *conn_accept(connection *conn) { int fd; unsigned int len; struct sockaddr_in sin; in46addr addr; connection *newconn; if (!FD_ISSET(conn->fd, &readfds)) { return (connection *) NULL; } len = sizeof(sin); fd = accept(conn->fd, (struct sockaddr *) &sin, &len); if (fd < 0) { return (connection *) NULL; } if (fcntl(fd, F_SETFL, FNDELAY)) { perror("fcntl"); close(fd); return NULL; } newconn = flist; flist = (connection *)newconn->chain.next; newconn->fd = fd; newconn->chain.name = (char *) NULL; newconn->udpbuf = (char *) NULL; addr.in.addr = sin.sin_addr; addr.ipv6 = FALSE; newconn->addr = ipa_new(&addr); newconn->port = ntohs(sin.sin_port); newconn->at = -1; FD_SET(fd, &infds); FD_SET(fd, &outfds); FD_CLR(fd, &readfds); FD_SET(fd, &writefds); if (fd > maxfd) { maxfd = fd; } return newconn; } int conn_udpreceive(connection *conn, char *buffer, int size, char **host, int *port) { if (FD_ISSET(conn->fd, &readfds)) { struct sockaddr_in from; unsigned int fromlen; int sz; fromlen = sizeof(struct sockaddr_in); sz = recvfrom(conn->fd, buffer, size, 0, (struct sockaddr *) &from, &fromlen); if (sz < 0) { perror("recvfrom"); return sz; } *host = inet_ntoa(from.sin_addr); *port = ntohs(from.sin_port); return sz; } return -1; } #endif # define CONN_READF 0x01 /* read flag set */ # define CONN_WRITEF 0x02 /* write flag set */ # define CONN_WAITF 0x04 /* wait flag set */ # define CONN_UCHAL 0x08 /* UDP challenge issued */ # define CONN_UCHAN 0x10 /* UDP channel established */ # define CONN_ADDR 0x20 /* has an address */ /* * NAME: conn->export() * DESCRIPTION: export a connection */ bool conn_export(connection *conn, int *fd, unsigned short *port, short *at, int *npkts, int *bufsz, char **buf, char *flags) { *fd = conn->fd; *port = conn->port; if (conn->fd >= 0) { *flags = 0; *at = conn->at; *npkts = conn->npkts; *bufsz = conn->bufsz; *buf = conn->udpbuf; if (FD_ISSET(conn->fd, &readfds)) { *flags |= CONN_READF; } if (FD_ISSET(conn->fd, &writefds)) { *flags |= CONN_WRITEF; } if (FD_ISSET(conn->fd, &waitfds)) { *flags |= CONN_WAITF; } if (conn->udpbuf != (char *) NULL) { if (conn->chain.name != NULL) { *flags |= CONN_UCHAL; } else { *flags |= CONN_UCHAN; } } if (conn->addr != (ipaddr *) NULL) { *flags |= CONN_ADDR; } } return TRUE; } /* * NAME: conn->import() * DESCRIPTION: import a connection */ connection *conn_import(int fd, unsigned short port, short at, int npkts, int bufsz, char *buf, char flags, bool telnet) { # ifdef INET6 struct sockaddr_in6 sin; # else struct sockaddr_in sin; # endif socklen_t len; in46addr inaddr; connection *conn; if (fd >= 0) { len = sizeof(sin); if (getpeername(fd, (struct sockaddr *) &sin, &len) != 0) { if (errno == EBADF || errno == ENOTCONN || (flags & CONN_ADDR)) { return (connection *) NULL; } } else { ipa_close(fd); inaddr.ipv6 = FALSE; # ifdef INET6 if (sin.sin6_family == AF_INET6) { inaddr.in.addr6 = sin.sin6_addr; inaddr.ipv6 = TRUE; } else # endif inaddr.in.addr = ((struct sockaddr_in *) &sin)->sin_addr; } } else { closed++; } conn = flist; flist = (connection *) conn->chain.next; conn->fd = fd; conn->chain.name = (char *) NULL; conn->udpbuf = (char *) NULL; conn->addr = (ipaddr *) NULL; conn->bufsz = 0; conn->npkts = 0; conn->port = port; conn->at = -1; if (fd >= 0) { FD_SET(fd, &infds); FD_SET(fd, &outfds); if (flags & CONN_READF) { FD_SET(fd, &readfds); } if (flags & CONN_WRITEF) { FD_SET(fd, &writefds); } if (flags & CONN_WAITF) { FD_SET(fd, &waitfds); } if (fd > maxfd) { maxfd = fd; } if (at >= 0 && at >= ((telnet) ? ntdescs : nbdescs)) { at = -1; } conn->at = at; if (flags & CONN_ADDR) { conn->addr = ipa_new(&inaddr); } # ifndef NETWORK_EXTENSIONS if (at >= 0) { if (flags & CONN_UCHAL) { conn_udp(conn, buf, bufsz); } if (flags & CONN_UCHAN) { connection **hash; conn->bufsz = bufsz; m_static(); conn->udpbuf = ALLOC(char, BINBUF_SIZE); m_dynamic(); memcpy(conn->udpbuf, buf, bufsz); # ifdef INET6 if (inaddr.ipv6) { hash = &udphtab[(hashmem((char *) &inaddr.in.addr6, sizeof(struct in6_addr)) ^ conn->port) % udphtabsz]; } else # endif hash = &udphtab[((Uint) inaddr.in.addr.s_addr ^ conn->port) % udphtabsz]; conn->chain.next = (hte *) *hash; *hash = conn; } conn->npkts = npkts; npackets += npkts; } # endif } return conn; }