8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / krb5 / krb5kdc / network.c
blobfc16b389496ce32e308c1faf40275249ddc81d5e
1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /*
7 * kdc/network.c
9 * Copyright 1990,2000 by the Massachusetts Institute of Technology.
11 * Export of this software from the United States of America may
12 * require a specific license from the United States Government.
13 * It is the responsibility of any person or organization contemplating
14 * export to obtain such a license before exporting.
16 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
17 * distribute this software and its documentation for any purpose and
18 * without fee is hereby granted, provided that the above copyright
19 * notice appear in all copies and that both that copyright notice and
20 * this permission notice appear in supporting documentation, and that
21 * the name of M.I.T. not be used in advertising or publicity pertaining
22 * to distribution of the software without specific, written prior
23 * permission. Furthermore if you modify this software you must label
24 * your software as modified software and not distribute it in such a
25 * fashion that it might be confused with the original M.I.T. software.
26 * M.I.T. makes no representations about the suitability of
27 * this software for any purpose. It is provided "as is" without express
28 * or implied warranty.
31 * Network code for Kerberos v5 KDC.
34 #include "k5-int.h"
35 #include "com_err.h"
36 #include "kdc_util.h"
37 #include "extern.h"
38 #include "kdc5_err.h"
39 #include "adm_proto.h"
40 #include <sys/ioctl.h>
41 #include <syslog.h>
43 #include <stddef.h>
44 #include <ctype.h>
45 #include "port-sockets.h"
46 /* #include "socket-utils.h" */
48 #ifdef HAVE_NETINET_IN_H
49 #include <sys/types.h>
50 #include <netinet/in.h>
51 #include <sys/socket.h>
52 #ifdef HAVE_SYS_SOCKIO_H
53 /* for SIOCGIFCONF, etc. */
54 #include <sys/sockio.h>
55 #endif
56 #include <sys/time.h>
57 #include <libintl.h>
59 #if HAVE_SYS_SELECT_H
60 #include <sys/select.h>
61 #endif
62 #include <arpa/inet.h>
63 #include <inet/ip.h>
64 #include <inet/ip6.h>
66 #ifndef ARPHRD_ETHER /* OpenBSD breaks on multiple inclusions */
67 #include <net/if.h>
68 #endif
70 #ifdef HAVE_SYS_FILIO_H
71 #include <sys/filio.h> /* FIONBIO */
72 #endif
74 #include "fake-addrinfo.h"
76 /* Misc utility routines. */
77 static void
78 set_sa_port(struct sockaddr *addr, int port)
80 switch (addr->sa_family) {
81 case AF_INET:
82 sa2sin(addr)->sin_port = port;
83 break;
84 #ifdef KRB5_USE_INET6
85 case AF_INET6:
86 sa2sin6(addr)->sin6_port = port;
87 break;
88 #endif
89 default:
90 break;
94 static int ipv6_enabled()
96 #ifdef KRB5_USE_INET6
97 static int result = -1;
98 if (result == -1) {
99 int s;
100 s = socket(AF_INET6, SOCK_STREAM, 0);
101 if (s >= 0) {
102 result = 1;
103 close(s);
104 } else
105 result = 0;
107 return result;
108 #else
109 return 0;
110 #endif
113 static int
114 setreuseaddr(int sock, int value)
116 return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
119 #if defined(KRB5_USE_INET6) && defined(IPV6_V6ONLY)
120 static int
121 setv6only(int sock, int value)
123 return setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &value, sizeof(value));
125 #endif
128 static const char *paddr (struct sockaddr *sa)
130 static char buf[100];
131 char portbuf[10];
132 if (getnameinfo(sa, socklen(sa),
133 buf, sizeof(buf), portbuf, sizeof(portbuf),
134 NI_NUMERICHOST|NI_NUMERICSERV))
135 strcpy(buf, "<unprintable>");
136 else {
137 unsigned int len = sizeof(buf) - strlen(buf);
138 char *p = buf + strlen(buf);
139 if (len > 2+strlen(portbuf)) {
140 *p++ = '.';
141 len--;
142 strncpy(p, portbuf, len);
145 return buf;
148 /* KDC data. */
150 enum kdc_conn_type { CONN_UDP, CONN_TCP_LISTENER, CONN_TCP };
152 /* Per-connection info. */
153 struct connection {
154 int fd;
155 enum kdc_conn_type type;
156 void (*service)(struct connection *, const char *, int);
157 /* Solaris Kerberos: for auditing */
158 in_port_t port; /* local port */
159 union {
160 /* Type-specific information. */
161 struct {
162 int x;
163 } udp;
164 struct {
165 int x;
166 } tcp_listener;
167 struct {
168 /* connection */
169 struct sockaddr_storage addr_s;
170 socklen_t addrlen;
171 char addrbuf[56];
172 krb5_fulladdr faddr;
173 krb5_address kaddr;
174 /* incoming */
175 size_t bufsiz;
176 size_t offset;
177 char *buffer;
178 size_t msglen;
179 /* outgoing */
180 krb5_data *response;
181 unsigned char lenbuf[4];
182 sg_buf sgbuf[2];
183 sg_buf *sgp;
184 int sgnum;
185 /* crude denial-of-service avoidance support */
186 time_t start_time;
187 } tcp;
188 } u;
192 #define SET(TYPE) struct { TYPE *data; int n, max; }
194 /* Start at the top and work down -- this should allow for deletions
195 without disrupting the iteration, since we delete by overwriting
196 the element to be removed with the last element. */
197 #define FOREACH_ELT(set,idx,vvar) \
198 for (idx = set.n-1; idx >= 0 && (vvar = set.data[idx], 1); idx--)
200 #define GROW_SET(set, incr, tmpptr) \
201 (((int)(set.max + incr) < set.max \
202 || (((size_t)((int)(set.max + incr) * sizeof(set.data[0])) \
203 / sizeof(set.data[0])) \
204 != (set.max + incr))) \
205 ? 0 /* overflow */ \
206 : ((tmpptr = realloc(set.data, \
207 (int)(set.max + incr) * sizeof(set.data[0]))) \
208 ? (set.data = tmpptr, set.max += incr, 1) \
209 : 0))
211 /* 1 = success, 0 = failure */
212 #define ADD(set, val, tmpptr) \
213 ((set.n < set.max || GROW_SET(set, 10, tmpptr)) \
214 ? (set.data[set.n++] = val, 1) \
215 : 0)
217 #define DEL(set, idx) \
218 (set.data[idx] = set.data[--set.n], 0)
220 #define FREE_SET_DATA(set) if(set.data) free(set.data); \
221 (set.data = 0, set.max = 0)
224 /* Set<struct connection *> connections; */
225 static SET(struct connection *) connections;
226 #define n_sockets connections.n
227 #define conns connections.data
229 /* Set<u_short> udp_port_data, tcp_port_data; */
230 static SET(u_short) udp_port_data, tcp_port_data;
232 #include "cm.h"
234 static struct select_state sstate;
236 static krb5_error_code add_udp_port(int port)
238 int i;
239 void *tmp;
240 u_short val;
241 u_short s_port = port;
243 if (s_port != port)
244 return EINVAL;
246 FOREACH_ELT (udp_port_data, i, val)
247 if (s_port == val)
248 return 0;
249 if (!ADD(udp_port_data, s_port, tmp))
250 return ENOMEM;
251 return 0;
254 static krb5_error_code add_tcp_port(int port)
256 int i;
257 void *tmp;
258 u_short val;
259 u_short s_port = port;
261 if (s_port != port)
262 return EINVAL;
264 FOREACH_ELT (tcp_port_data, i, val)
265 if (s_port == val)
266 return 0;
267 if (!ADD(tcp_port_data, s_port, tmp))
268 return ENOMEM;
269 return 0;
273 #define USE_AF AF_INET
274 #define USE_TYPE SOCK_DGRAM
275 #define USE_PROTO 0
276 #define SOCKET_ERRNO errno
277 #include "foreachaddr.h"
279 struct socksetup {
280 const char *prog;
281 krb5_error_code retval;
284 static struct connection *
285 add_fd (struct socksetup *data, int sock, enum kdc_conn_type conntype,
286 void (*service)(struct connection *, const char *, int))
288 struct connection *newconn;
289 void *tmp;
291 newconn = malloc(sizeof(*newconn));
292 if (newconn == 0) {
293 data->retval = errno;
294 com_err(data->prog, errno,
295 gettext("cannot allocate storage for connection info"));
296 return 0;
298 if (!ADD(connections, newconn, tmp)) {
299 data->retval = errno;
300 com_err(data->prog, data->retval, gettext("cannot save socket info"));
301 free(newconn);
302 return 0;
305 memset(newconn, 0, sizeof(*newconn));
306 newconn->type = conntype;
307 newconn->fd = sock;
308 newconn->service = service;
309 return newconn;
312 static void process_packet(struct connection *, const char *, int);
313 static void accept_tcp_connection(struct connection *, const char *, int);
314 static void process_tcp_connection(struct connection *, const char *, int);
316 static struct connection *
317 add_udp_fd (struct socksetup *data, int sock)
319 return add_fd(data, sock, CONN_UDP, process_packet);
322 static struct connection *
323 add_tcp_listener_fd (struct socksetup *data, int sock)
325 return add_fd(data, sock, CONN_TCP_LISTENER, accept_tcp_connection);
328 static struct connection *
329 add_tcp_data_fd (struct socksetup *data, int sock)
331 return add_fd(data, sock, CONN_TCP, process_tcp_connection);
334 static void
335 delete_fd (struct connection *xconn)
337 struct connection *conn;
338 int i;
340 FOREACH_ELT(connections, i, conn)
341 if (conn == xconn) {
342 DEL(connections, i);
343 break;
345 free(xconn);
348 static int
349 setnbio(int sock)
351 static const int one = 1;
352 return ioctlsocket(sock, FIONBIO, (const void *)&one);
355 static int
356 setnolinger(int s)
358 static const struct linger ling = { 0, 0 };
359 return setsockopt(s, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
362 /* Returns -1 or socket fd. */
363 static int
364 setup_a_tcp_listener(struct socksetup *data, struct sockaddr *addr)
366 int sock;
368 sock = socket(addr->sa_family, SOCK_STREAM, 0);
369 if (sock == -1) {
370 com_err(data->prog, errno,
371 gettext("Cannot create TCP server socket on %s"),
372 paddr(addr));
373 return -1;
376 * Solaris Kerberos: noticed that there where bind problems for tcp sockets
377 * if kdc restarted quickly. Setting SO_REUSEADDR allowed binds to succeed.
379 if (setreuseaddr(sock, 1) < 0) {
380 com_err(data->prog, errno,
381 gettext("enabling SO_REUSEADDR on TCP socket"));
382 close(sock);
383 return -1;
385 if (bind(sock, addr, socklen(addr)) == -1) {
386 com_err(data->prog, errno,
387 gettext("Cannot bind TCP server socket on %s"), paddr(addr));
388 close(sock);
389 return -1;
391 if (listen(sock, 5) < 0) {
392 com_err(data->prog, errno,
393 gettext("Cannot listen on TCP server socket on %s"),
394 paddr(addr));
395 close(sock);
396 return -1;
398 if (setnbio(sock)) {
399 com_err(data->prog, errno,
400 gettext("cannot set listening tcp socket on %s non-blocking"),
401 paddr(addr));
402 close(sock);
403 return -1;
405 if (setnolinger(sock)) {
406 com_err(data->prog, errno,
407 gettext("disabling SO_LINGER on TCP socket on %s"),
408 paddr(addr));
409 close(sock);
410 return -1;
412 return sock;
415 static int
416 setup_tcp_listener_ports(struct socksetup *data)
418 struct sockaddr_in sin4;
419 #ifdef KRB5_USE_INET6
420 struct sockaddr_in6 sin6;
421 #endif
422 int i, port;
424 memset(&sin4, 0, sizeof(sin4));
425 sin4.sin_family = AF_INET;
426 #ifdef HAVE_SA_LEN
427 sin4.sin_len = sizeof(sin4);
428 #endif
429 sin4.sin_addr.s_addr = INADDR_ANY;
431 #ifdef KRB5_USE_INET6
432 memset(&sin6, 0, sizeof(sin6));
433 sin6.sin6_family = AF_INET6;
434 #ifdef SIN6_LEN
435 sin6.sin6_len = sizeof(sin6);
436 #endif
437 sin6.sin6_addr = in6addr_any;
438 #endif
440 FOREACH_ELT (tcp_port_data, i, port) {
441 int s4, s6;
443 set_sa_port((struct sockaddr *)&sin4, htons(port));
444 if (!ipv6_enabled()) {
445 s4 = setup_a_tcp_listener(data, (struct sockaddr *)&sin4);
446 if (s4 < 0)
447 return -1;
448 s6 = -1;
449 } else {
450 #ifndef KRB5_USE_INET6
451 abort();
452 #else
453 s4 = s6 = -1;
455 set_sa_port((struct sockaddr *)&sin6, htons(port));
457 s6 = setup_a_tcp_listener(data, (struct sockaddr *)&sin6);
458 if (s6 < 0)
459 return -1;
460 #ifdef IPV6_V6ONLY
461 if (setv6only(s6, 0))
462 com_err(data->prog, errno,
463 gettext("setsockopt(IPV6_V6ONLY,0) failed"));
464 #endif
466 s4 = setup_a_tcp_listener(data, (struct sockaddr *)&sin4);
467 #endif /* KRB5_USE_INET6 */
470 /* Sockets are created, prepare to listen on them. */
471 if (s4 >= 0) {
472 FD_SET(s4, &sstate.rfds);
473 if (s4 >= sstate.max)
474 sstate.max = s4 + 1;
475 if (add_tcp_listener_fd(data, s4) == 0)
476 close(s4);
477 else
478 krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s",
479 s4, paddr((struct sockaddr *)&sin4));
481 #ifdef KRB5_USE_INET6
482 if (s6 >= 0) {
483 FD_SET(s6, &sstate.rfds);
484 if (s6 >= sstate.max)
485 sstate.max = s6 + 1;
486 if (add_tcp_listener_fd(data, s6) == 0) {
487 close(s6);
488 s6 = -1;
489 } else
490 krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s",
491 s6, paddr((struct sockaddr *)&sin6));
492 if (s4 < 0)
493 krb5_klog_syslog(LOG_INFO,
494 "assuming IPv6 socket accepts IPv4");
496 #endif
498 return 0;
501 static int
502 setup_udp_port(void *P_data, struct sockaddr *addr)
504 struct socksetup *data = P_data;
505 int sock = -1, i;
506 char haddrbuf[NI_MAXHOST];
507 int err;
508 u_short port;
510 err = getnameinfo(addr, socklen(addr), haddrbuf, sizeof(haddrbuf),
511 0, 0, NI_NUMERICHOST);
512 if (err)
513 strcpy(haddrbuf, "<unprintable>");
515 switch (addr->sa_family) {
516 case AF_INET:
517 break;
518 #ifdef AF_INET6
519 case AF_INET6:
520 #ifdef KRB5_USE_INET6
521 break;
522 #else
524 static int first = 1;
525 if (first) {
526 krb5_klog_syslog (LOG_INFO, "skipping local ipv6 addresses");
527 first = 0;
529 return 0;
531 #endif
532 #endif
533 #ifdef AF_LINK /* some BSD systems, AIX */
534 case AF_LINK:
535 return 0;
536 #endif
537 #ifdef AF_DLI /* Direct Link Interface - DEC Ultrix/OSF1 link layer? */
538 case AF_DLI:
539 return 0;
540 #endif
541 default:
542 krb5_klog_syslog (LOG_INFO,
543 "skipping unrecognized local address family %d",
544 addr->sa_family);
545 return 0;
548 FOREACH_ELT (udp_port_data, i, port) {
549 sock = socket (addr->sa_family, SOCK_DGRAM, 0);
550 if (sock == -1) {
551 data->retval = errno;
552 com_err(data->prog, data->retval,
553 gettext("Cannot create server socket for port %d address %s"),
554 port, haddrbuf);
555 return 1;
557 set_sa_port(addr, htons(port));
558 if (bind (sock, (struct sockaddr *)addr, socklen (addr)) == -1) {
559 data->retval = errno;
560 com_err(data->prog, data->retval,
561 gettext("Cannot bind server socket to port %d address %s"),
562 port, haddrbuf);
563 return 1;
565 FD_SET (sock, &sstate.rfds);
566 if (sock >= sstate.max)
567 sstate.max = sock + 1;
568 krb5_klog_syslog (LOG_INFO, "listening on fd %d: udp %s", sock,
569 paddr((struct sockaddr *)addr));
570 if (add_udp_fd (data, sock) == 0)
571 return 1;
573 return 0;
576 #if 1
577 static void klog_handler(const void *data, size_t len)
579 static char buf[BUFSIZ];
580 static int bufoffset;
581 void *p;
583 #define flush_buf() \
584 (bufoffset \
585 ? (((buf[0] == 0 || buf[0] == '\n') \
586 ? (fork()==0?abort():(void)0) \
587 : (void)0), \
588 krb5_klog_syslog(LOG_INFO, "%s", buf), \
589 memset(buf, 0, sizeof(buf)), \
590 bufoffset = 0) \
591 : 0)
593 p = memchr(data, 0, len);
594 if (p)
595 len = (const char *)p - (const char *)data;
596 scan_for_newlines:
597 if (len == 0)
598 return;
599 p = memchr(data, '\n', len);
600 if (p) {
601 if (p != data)
602 klog_handler(data, (size_t)((const char *)p - (const char *)data));
603 flush_buf();
604 len -= ((const char *)p - (const char *)data) + 1;
605 data = 1 + (const char *)p;
606 goto scan_for_newlines;
607 } else if (len > sizeof(buf) - 1 || len + bufoffset > sizeof(buf) - 1) {
608 size_t x = sizeof(buf) - len - 1;
609 klog_handler(data, x);
610 flush_buf();
611 len -= x;
612 data = (const char *)data + x;
613 goto scan_for_newlines;
614 } else {
615 memcpy(buf + bufoffset, data, len);
616 bufoffset += len;
619 #endif
621 /* XXX */
622 extern int krb5int_debug_sendto_kdc;
623 extern void (*krb5int_sendtokdc_debug_handler)(const void*, size_t);
625 krb5_error_code
626 setup_network(const char *prog)
628 struct socksetup setup_data;
629 krb5_error_code retval;
630 char *cp;
631 int i, port;
633 FD_ZERO(&sstate.rfds);
634 FD_ZERO(&sstate.wfds);
635 FD_ZERO(&sstate.xfds);
636 sstate.max = 0;
638 /* krb5int_debug_sendto_kdc = 1; */
639 krb5int_sendtokdc_debug_handler = klog_handler;
641 /* Handle each realm's ports */
642 for (i=0; i<kdc_numrealms; i++) {
643 cp = kdc_realmlist[i]->realm_ports;
644 while (cp && *cp) {
645 if (*cp == ',' || isspace((int) *cp)) {
646 cp++;
647 continue;
649 port = strtol(cp, &cp, 10);
650 if (cp == 0)
651 break;
652 retval = add_udp_port(port);
653 if (retval)
654 return retval;
657 cp = kdc_realmlist[i]->realm_tcp_ports;
658 while (cp && *cp) {
659 if (*cp == ',' || isspace((int) *cp)) {
660 cp++;
661 continue;
663 port = strtol(cp, &cp, 10);
664 if (cp == 0)
665 break;
666 retval = add_tcp_port(port);
667 if (retval)
668 return retval;
672 setup_data.prog = prog;
673 setup_data.retval = 0;
674 krb5_klog_syslog (LOG_INFO, "setting up network...");
675 /* To do: Use RFC 2292 interface (or follow-on) and IPV6_PKTINFO,
676 so we might need only one UDP socket; fall back to binding
677 sockets on each address only if IPV6_PKTINFO isn't
678 supported. */
679 if (foreach_localaddr (&setup_data, setup_udp_port, 0, 0)) {
680 return setup_data.retval;
682 setup_tcp_listener_ports(&setup_data);
683 krb5_klog_syslog (LOG_INFO, "set up %d sockets", n_sockets);
684 if (n_sockets == 0) {
685 com_err(prog, 0, gettext("no sockets set up?"));
686 exit (1);
689 return 0;
692 static void init_addr(krb5_fulladdr *faddr, struct sockaddr *sa)
694 switch (sa->sa_family) {
695 case AF_INET:
696 faddr->address->addrtype = ADDRTYPE_INET;
697 faddr->address->length = IPV4_ADDR_LEN;
698 faddr->address->contents = (krb5_octet *) &sa2sin(sa)->sin_addr;
699 faddr->port = ntohs(sa2sin(sa)->sin_port);
700 break;
701 #ifdef KRB5_USE_INET6
702 case AF_INET6:
703 if (IN6_IS_ADDR_V4MAPPED(&sa2sin6(sa)->sin6_addr)) {
704 faddr->address->addrtype = ADDRTYPE_INET;
705 faddr->address->length = IPV4_ADDR_LEN;
706 /* offset to RAM address of ipv4 part of ipv6 address */
707 faddr->address->contents = (IPV6_ADDR_LEN - IPV4_ADDR_LEN) +
708 (krb5_octet *) &sa2sin6(sa)->sin6_addr;
709 } else {
710 faddr->address->addrtype = ADDRTYPE_INET6;
711 faddr->address->length = IPV6_ADDR_LEN;
712 faddr->address->contents = (krb5_octet *) &sa2sin6(sa)->sin6_addr;
714 faddr->port = ntohs(sa2sin6(sa)->sin6_port);
715 break;
716 #endif
717 default:
718 faddr->address->addrtype = -1;
719 faddr->address->length = 0;
720 faddr->address->contents = 0;
721 faddr->port = 0;
722 break;
726 static void process_packet(struct connection *conn, const char *prog,
727 int selflags)
729 int cc;
730 socklen_t saddr_len;
731 krb5_fulladdr faddr;
732 krb5_error_code retval;
733 struct sockaddr_storage saddr;
734 krb5_address addr;
735 krb5_data request;
736 krb5_data *response;
737 char pktbuf[MAX_DGRAM_SIZE];
738 int port_fd = conn->fd;
740 response = NULL;
741 saddr_len = sizeof(saddr);
742 cc = recvfrom(port_fd, pktbuf, sizeof(pktbuf), 0,
743 (struct sockaddr *)&saddr, &saddr_len);
744 if (cc == -1) {
745 if (errno != EINTR
746 /* This is how Linux indicates that a previous
747 transmission was refused, e.g., if the client timed out
748 before getting the response packet. */
749 && errno != ECONNREFUSED
751 com_err(prog, errno, gettext("while receiving from network"));
752 return;
754 if (!cc)
755 return; /* zero-length packet? */
757 request.length = cc;
758 request.data = pktbuf;
759 faddr.address = &addr;
760 init_addr(&faddr, ss2sa(&saddr));
761 /* this address is in net order */
762 if ((retval = dispatch(&request, &faddr, &response))) {
763 com_err(prog, retval, gettext("while dispatching (udp)"));
764 return;
766 cc = sendto(port_fd, response->data, (socklen_t) response->length, 0,
767 (struct sockaddr *)&saddr, saddr_len);
768 if (cc == -1) {
769 char addrbuf[46];
770 krb5_free_data(kdc_context, response);
771 if (inet_ntop(((struct sockaddr *)&saddr)->sa_family,
772 addr.contents, addrbuf, sizeof(addrbuf)) == 0) {
773 strcpy(addrbuf, "?");
775 com_err(prog, errno, gettext("while sending reply to %s/%d"),
776 addrbuf, faddr.port);
777 return;
779 if (cc != response->length) {
780 krb5_free_data(kdc_context, response);
781 com_err(prog, 0, gettext("short reply write %d vs %d\n"),
782 response->length, cc);
783 return;
785 krb5_free_data(kdc_context, response);
786 return;
789 static int tcp_data_counter;
790 /* Solaris kerberos: getting this value from elsewhere */
791 extern int max_tcp_data_connections;
793 static void kill_tcp_connection(struct connection *);
795 static void accept_tcp_connection(struct connection *conn, const char *prog,
796 int selflags)
798 int s;
799 struct sockaddr_storage addr_s;
800 struct sockaddr *addr = (struct sockaddr *)&addr_s;
801 socklen_t addrlen = sizeof(addr_s);
802 struct socksetup sockdata;
803 struct connection *newconn;
804 char tmpbuf[10];
806 s = accept(conn->fd, addr, &addrlen);
807 if (s < 0)
808 return;
809 setnbio(s), setnolinger(s);
811 sockdata.prog = prog;
812 sockdata.retval = 0;
814 newconn = add_tcp_data_fd(&sockdata, s);
815 if (newconn == 0)
816 return;
818 if (getnameinfo((struct sockaddr *)&addr_s, addrlen,
819 newconn->u.tcp.addrbuf, sizeof(newconn->u.tcp.addrbuf),
820 tmpbuf, sizeof(tmpbuf),
821 NI_NUMERICHOST | NI_NUMERICSERV))
822 strcpy(newconn->u.tcp.addrbuf, "???");
823 else {
824 char *p, *end;
825 p = newconn->u.tcp.addrbuf;
826 end = p + sizeof(newconn->u.tcp.addrbuf);
827 p += strlen(p);
828 if (end - p > 2 + strlen(tmpbuf)) {
829 *p++ = '.';
830 strcpy(p, tmpbuf);
833 #if 0
834 krb5_klog_syslog(LOG_INFO, "accepted TCP connection on socket %d from %s",
835 s, newconn->u.tcp.addrbuf);
836 #endif
838 newconn->u.tcp.addr_s = addr_s;
839 newconn->u.tcp.addrlen = addrlen;
840 newconn->u.tcp.bufsiz = 1024 * 1024;
841 newconn->u.tcp.buffer = malloc(newconn->u.tcp.bufsiz);
842 newconn->u.tcp.start_time = time(0);
844 if (++tcp_data_counter > max_tcp_data_connections) {
845 struct connection *oldest_tcp = NULL;
846 struct connection *c;
847 int i;
849 krb5_klog_syslog(LOG_INFO, "too many connections");
851 FOREACH_ELT (connections, i, c) {
852 if (c->type != CONN_TCP)
853 continue;
854 if (c == newconn)
855 continue;
856 #if 0
857 krb5_klog_syslog(LOG_INFO, "fd %d started at %ld", c->fd,
858 c->u.tcp.start_time);
859 #endif
860 if (oldest_tcp == NULL
861 || oldest_tcp->u.tcp.start_time > c->u.tcp.start_time)
862 oldest_tcp = c;
864 if (oldest_tcp != NULL) {
865 krb5_klog_syslog(LOG_INFO, "dropping tcp fd %d from %s",
866 oldest_tcp->fd, oldest_tcp->u.tcp.addrbuf);
867 kill_tcp_connection(oldest_tcp);
868 oldest_tcp = NULL;
871 if (newconn->u.tcp.buffer == 0) {
872 com_err(prog, errno, gettext("allocating buffer for new TCP session from %s"),
873 newconn->u.tcp.addrbuf);
874 delete_fd(newconn);
875 close(s);
876 tcp_data_counter--;
877 return;
879 newconn->u.tcp.offset = 0;
880 newconn->u.tcp.faddr.address = &newconn->u.tcp.kaddr;
881 init_addr(&newconn->u.tcp.faddr, ss2sa(&newconn->u.tcp.addr_s));
882 SG_SET(&newconn->u.tcp.sgbuf[0], newconn->u.tcp.lenbuf, 4);
883 SG_SET(&newconn->u.tcp.sgbuf[1], 0, 0);
885 FD_SET(s, &sstate.rfds);
886 if (sstate.max <= s)
887 sstate.max = s + 1;
890 static void
891 kill_tcp_connection(struct connection *conn)
893 if (conn->u.tcp.response)
894 krb5_free_data(kdc_context, conn->u.tcp.response);
895 if (conn->u.tcp.buffer)
896 free(conn->u.tcp.buffer);
897 FD_CLR(conn->fd, &sstate.rfds);
898 FD_CLR(conn->fd, &sstate.wfds);
899 if (sstate.max == conn->fd + 1)
900 while (sstate.max > 0
901 && ! FD_ISSET(sstate.max-1, &sstate.rfds)
902 && ! FD_ISSET(sstate.max-1, &sstate.wfds)
903 /* && ! FD_ISSET(sstate.max-1, &sstate.xfds) */
905 sstate.max--;
906 close(conn->fd);
907 conn->fd = -1;
908 delete_fd(conn);
909 tcp_data_counter--;
912 static krb5_error_code
913 make_toolong_error (krb5_data **out)
915 krb5_error errpkt;
916 krb5_error_code retval;
917 krb5_data *scratch;
919 retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec);
920 if (retval)
921 return retval;
922 errpkt.error = KRB_ERR_FIELD_TOOLONG;
923 errpkt.server = tgs_server;
924 errpkt.client = NULL;
925 errpkt.cusec = 0;
926 errpkt.ctime = 0;
927 errpkt.text.length = 0;
928 errpkt.text.data = 0;
929 errpkt.e_data.length = 0;
930 errpkt.e_data.data = 0;
931 scratch = malloc(sizeof(*scratch));
932 if (scratch == NULL)
933 return ENOMEM;
934 retval = krb5_mk_error(kdc_context, &errpkt, scratch);
935 if (retval) {
936 free(scratch);
937 return retval;
940 *out = scratch;
941 return 0;
944 static void
945 process_tcp_connection(struct connection *conn, const char *prog, int selflags)
947 if (selflags & SSF_WRITE) {
948 ssize_t nwrote;
949 SOCKET_WRITEV_TEMP tmp;
951 nwrote = SOCKET_WRITEV(conn->fd, conn->u.tcp.sgp, conn->u.tcp.sgnum,
952 tmp);
953 if (nwrote < 0) {
954 goto kill_tcp_connection;
956 if (nwrote == 0)
957 /* eof */
958 goto kill_tcp_connection;
959 while (nwrote) {
960 sg_buf *sgp = conn->u.tcp.sgp;
961 if (nwrote < SG_LEN(sgp)) {
962 SG_ADVANCE(sgp, nwrote);
963 nwrote = 0;
964 } else {
965 nwrote -= SG_LEN(sgp);
966 conn->u.tcp.sgp++;
967 conn->u.tcp.sgnum--;
968 if (conn->u.tcp.sgnum == 0 && nwrote != 0)
969 abort();
972 if (conn->u.tcp.sgnum == 0) {
973 /* finished sending */
974 /* We should go back to reading, though if we sent a
975 FIELD_TOOLONG error in reply to a length with the high
976 bit set, RFC 4120 says we have to close the TCP
977 stream. */
978 goto kill_tcp_connection;
980 } else if (selflags & SSF_READ) {
981 /* Read message length and data into one big buffer, already
982 allocated at connect time. If we have a complete message,
983 we stop reading, so we should only be here if there is no
984 data in the buffer, or only an incomplete message. */
985 size_t len;
986 ssize_t nread;
987 if (conn->u.tcp.offset < 4) {
988 /* msglen has not been computed */
989 /* XXX Doing at least two reads here, letting the kernel
990 worry about buffering. It'll be faster when we add
991 code to manage the buffer here. */
992 len = 4 - conn->u.tcp.offset;
993 nread = SOCKET_READ(conn->fd,
994 conn->u.tcp.buffer + conn->u.tcp.offset, len);
995 if (nread < 0)
996 /* error */
997 goto kill_tcp_connection;
998 if (nread == 0)
999 /* eof */
1000 goto kill_tcp_connection;
1001 conn->u.tcp.offset += nread;
1002 if (conn->u.tcp.offset == 4) {
1003 unsigned char *p = (unsigned char *)conn->u.tcp.buffer;
1004 conn->u.tcp.msglen = ((p[0] << 24)
1005 | (p[1] << 16)
1006 | (p[2] << 8)
1007 | p[3]);
1008 if (conn->u.tcp.msglen > conn->u.tcp.bufsiz - 4) {
1009 krb5_error_code err;
1010 /* message too big */
1011 krb5_klog_syslog(LOG_ERR, "TCP client %s wants %lu bytes, cap is %lu",
1012 conn->u.tcp.addrbuf, (unsigned long) conn->u.tcp.msglen,
1013 (unsigned long) conn->u.tcp.bufsiz - 4);
1014 /* XXX Should return an error. */
1015 err = make_toolong_error (&conn->u.tcp.response);
1016 if (err) {
1017 krb5_klog_syslog(LOG_ERR,
1018 "error constructing KRB_ERR_FIELD_TOOLONG error! %s",
1019 error_message(err));
1020 goto kill_tcp_connection;
1022 goto have_response;
1025 } else {
1026 /* msglen known */
1027 krb5_data request;
1028 krb5_error_code err;
1030 len = conn->u.tcp.msglen - (conn->u.tcp.offset - 4);
1031 nread = SOCKET_READ(conn->fd,
1032 conn->u.tcp.buffer + conn->u.tcp.offset, len);
1033 if (nread < 0)
1034 /* error */
1035 goto kill_tcp_connection;
1036 if (nread == 0)
1037 /* eof */
1038 goto kill_tcp_connection;
1039 conn->u.tcp.offset += nread;
1040 if (conn->u.tcp.offset < conn->u.tcp.msglen + 4)
1041 return;
1042 /* have a complete message, and exactly one message */
1043 request.length = conn->u.tcp.msglen;
1044 request.data = conn->u.tcp.buffer + 4;
1045 err = dispatch(&request, &conn->u.tcp.faddr,
1046 &conn->u.tcp.response);
1047 if (err) {
1048 com_err(prog, err, gettext("while dispatching (tcp)"));
1049 goto kill_tcp_connection;
1051 have_response:
1052 conn->u.tcp.lenbuf[0] = 0xff & (conn->u.tcp.response->length >> 24);
1053 conn->u.tcp.lenbuf[1] = 0xff & (conn->u.tcp.response->length >> 16);
1054 conn->u.tcp.lenbuf[2] = 0xff & (conn->u.tcp.response->length >> 8);
1055 conn->u.tcp.lenbuf[3] = 0xff & (conn->u.tcp.response->length >> 0);
1056 SG_SET(&conn->u.tcp.sgbuf[1], conn->u.tcp.response->data,
1057 conn->u.tcp.response->length);
1058 conn->u.tcp.sgp = conn->u.tcp.sgbuf;
1059 conn->u.tcp.sgnum = 2;
1060 FD_CLR(conn->fd, &sstate.rfds);
1061 FD_SET(conn->fd, &sstate.wfds);
1063 } else
1064 abort();
1066 return;
1068 kill_tcp_connection:
1069 kill_tcp_connection(conn);
1072 static void service_conn(struct connection *conn, const char *prog,
1073 int selflags)
1075 conn->service(conn, prog, selflags);
1078 krb5_error_code
1079 listen_and_process(const char *prog)
1081 int nfound;
1082 /* This struct contains 3 fd_set objects; on some platforms, they
1083 can be rather large. Making this static avoids putting all
1084 that junk on the stack. */
1085 static struct select_state sout;
1086 int i, sret;
1087 krb5_error_code err;
1089 if (conns == (struct connection **) NULL)
1090 return KDC5_NONET;
1092 while (!signal_requests_exit) {
1093 if (signal_requests_hup) {
1094 krb5_klog_reopen(kdc_context);
1095 signal_requests_hup = 0;
1097 sstate.end_time.tv_sec = sstate.end_time.tv_usec = 0;
1098 err = krb5int_cm_call_select(&sstate, &sout, &sret);
1099 if (err) {
1100 com_err(prog, err, gettext("while selecting for network input(1)"));
1101 continue;
1103 if (sret == -1) {
1104 if (errno != EINTR)
1105 com_err(prog, errno, gettext("while selecting for network input(2)"));
1106 continue;
1108 nfound = sret;
1109 for (i=0; i<n_sockets && nfound > 0; i++) {
1110 int sflags = 0;
1111 if (conns[i]->fd < 0)
1112 abort();
1113 if (FD_ISSET(conns[i]->fd, &sout.rfds))
1114 sflags |= SSF_READ, nfound--;
1115 if (FD_ISSET(conns[i]->fd, &sout.wfds))
1116 sflags |= SSF_WRITE, nfound--;
1117 if (sflags)
1118 service_conn(conns[i], prog, sflags);
1121 return 0;
1124 krb5_error_code
1125 closedown_network(const char *prog)
1127 int i;
1128 struct connection *conn;
1130 if (conns == (struct connection **) NULL)
1131 return KDC5_NONET;
1133 FOREACH_ELT (connections, i, conn) {
1134 if (conn->fd >= 0)
1135 (void) close(conn->fd);
1136 DEL (connections, i);
1137 /* There may also be per-connection data in the tcp structure
1138 (tcp.buffer, tcp.response) that we're not freeing here.
1139 That should only happen if we quit with a connection in
1140 progress. */
1141 free(conn);
1143 FREE_SET_DATA(connections);
1144 FREE_SET_DATA(udp_port_data);
1145 FREE_SET_DATA(tcp_port_data);
1147 return 0;
1150 #endif /* INET */