dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / krb5 / krb5kdc / network.c
bloba2e4b063069b32ba38bd48f78396ecd1c9b0a545
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 = reallocarray(set.data, set.max + incr, sizeof (set.data[0]))) \
207 ? (set.data = tmpptr, set.max += incr, 1) \
208 : 0))
210 /* 1 = success, 0 = failure */
211 #define ADD(set, val, tmpptr) \
212 ((set.n < set.max || GROW_SET(set, 10, tmpptr)) \
213 ? (set.data[set.n++] = val, 1) \
214 : 0)
216 #define DEL(set, idx) \
217 (set.data[idx] = set.data[--set.n], 0)
219 #define FREE_SET_DATA(set) if(set.data) free(set.data); \
220 (set.data = 0, set.max = 0)
223 /* Set<struct connection *> connections; */
224 static SET(struct connection *) connections;
225 #define n_sockets connections.n
226 #define conns connections.data
228 /* Set<u_short> udp_port_data, tcp_port_data; */
229 static SET(u_short) udp_port_data, tcp_port_data;
231 #include "cm.h"
233 static struct select_state sstate;
235 static krb5_error_code add_udp_port(int port)
237 int i;
238 void *tmp;
239 u_short val;
240 u_short s_port = port;
242 if (s_port != port)
243 return EINVAL;
245 FOREACH_ELT (udp_port_data, i, val)
246 if (s_port == val)
247 return 0;
248 if (!ADD(udp_port_data, s_port, tmp))
249 return ENOMEM;
250 return 0;
253 static krb5_error_code add_tcp_port(int port)
255 int i;
256 void *tmp;
257 u_short val;
258 u_short s_port = port;
260 if (s_port != port)
261 return EINVAL;
263 FOREACH_ELT (tcp_port_data, i, val)
264 if (s_port == val)
265 return 0;
266 if (!ADD(tcp_port_data, s_port, tmp))
267 return ENOMEM;
268 return 0;
272 #define USE_AF AF_INET
273 #define USE_TYPE SOCK_DGRAM
274 #define USE_PROTO 0
275 #define SOCKET_ERRNO errno
276 #include "foreachaddr.h"
278 struct socksetup {
279 const char *prog;
280 krb5_error_code retval;
283 static struct connection *
284 add_fd (struct socksetup *data, int sock, enum kdc_conn_type conntype,
285 void (*service)(struct connection *, const char *, int))
287 struct connection *newconn;
288 void *tmp;
290 newconn = malloc(sizeof(*newconn));
291 if (newconn == 0) {
292 data->retval = errno;
293 com_err(data->prog, errno,
294 gettext("cannot allocate storage for connection info"));
295 return 0;
297 if (!ADD(connections, newconn, tmp)) {
298 data->retval = errno;
299 com_err(data->prog, data->retval, gettext("cannot save socket info"));
300 free(newconn);
301 return 0;
304 memset(newconn, 0, sizeof(*newconn));
305 newconn->type = conntype;
306 newconn->fd = sock;
307 newconn->service = service;
308 return newconn;
311 static void process_packet(struct connection *, const char *, int);
312 static void accept_tcp_connection(struct connection *, const char *, int);
313 static void process_tcp_connection(struct connection *, const char *, int);
315 static struct connection *
316 add_udp_fd (struct socksetup *data, int sock)
318 return add_fd(data, sock, CONN_UDP, process_packet);
321 static struct connection *
322 add_tcp_listener_fd (struct socksetup *data, int sock)
324 return add_fd(data, sock, CONN_TCP_LISTENER, accept_tcp_connection);
327 static struct connection *
328 add_tcp_data_fd (struct socksetup *data, int sock)
330 return add_fd(data, sock, CONN_TCP, process_tcp_connection);
333 static void
334 delete_fd (struct connection *xconn)
336 struct connection *conn;
337 int i;
339 FOREACH_ELT(connections, i, conn)
340 if (conn == xconn) {
341 DEL(connections, i);
342 break;
344 free(xconn);
347 static int
348 setnbio(int sock)
350 static const int one = 1;
351 return ioctlsocket(sock, FIONBIO, (const void *)&one);
354 static int
355 setnolinger(int s)
357 static const struct linger ling = { 0, 0 };
358 return setsockopt(s, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
361 /* Returns -1 or socket fd. */
362 static int
363 setup_a_tcp_listener(struct socksetup *data, struct sockaddr *addr)
365 int sock;
367 sock = socket(addr->sa_family, SOCK_STREAM, 0);
368 if (sock == -1) {
369 com_err(data->prog, errno,
370 gettext("Cannot create TCP server socket on %s"),
371 paddr(addr));
372 return -1;
375 * Solaris Kerberos: noticed that there where bind problems for tcp sockets
376 * if kdc restarted quickly. Setting SO_REUSEADDR allowed binds to succeed.
378 if (setreuseaddr(sock, 1) < 0) {
379 com_err(data->prog, errno,
380 gettext("enabling SO_REUSEADDR on TCP socket"));
381 close(sock);
382 return -1;
384 if (bind(sock, addr, socklen(addr)) == -1) {
385 com_err(data->prog, errno,
386 gettext("Cannot bind TCP server socket on %s"), paddr(addr));
387 close(sock);
388 return -1;
390 if (listen(sock, 5) < 0) {
391 com_err(data->prog, errno,
392 gettext("Cannot listen on TCP server socket on %s"),
393 paddr(addr));
394 close(sock);
395 return -1;
397 if (setnbio(sock)) {
398 com_err(data->prog, errno,
399 gettext("cannot set listening tcp socket on %s non-blocking"),
400 paddr(addr));
401 close(sock);
402 return -1;
404 if (setnolinger(sock)) {
405 com_err(data->prog, errno,
406 gettext("disabling SO_LINGER on TCP socket on %s"),
407 paddr(addr));
408 close(sock);
409 return -1;
411 return sock;
414 static int
415 setup_tcp_listener_ports(struct socksetup *data)
417 struct sockaddr_in sin4;
418 #ifdef KRB5_USE_INET6
419 struct sockaddr_in6 sin6;
420 #endif
421 int i, port;
423 memset(&sin4, 0, sizeof(sin4));
424 sin4.sin_family = AF_INET;
425 #ifdef HAVE_SA_LEN
426 sin4.sin_len = sizeof(sin4);
427 #endif
428 sin4.sin_addr.s_addr = INADDR_ANY;
430 #ifdef KRB5_USE_INET6
431 memset(&sin6, 0, sizeof(sin6));
432 sin6.sin6_family = AF_INET6;
433 #ifdef SIN6_LEN
434 sin6.sin6_len = sizeof(sin6);
435 #endif
436 sin6.sin6_addr = in6addr_any;
437 #endif
439 FOREACH_ELT (tcp_port_data, i, port) {
440 int s4, s6;
442 set_sa_port((struct sockaddr *)&sin4, htons(port));
443 if (!ipv6_enabled()) {
444 s4 = setup_a_tcp_listener(data, (struct sockaddr *)&sin4);
445 if (s4 < 0)
446 return -1;
447 s6 = -1;
448 } else {
449 #ifndef KRB5_USE_INET6
450 abort();
451 #else
452 s4 = s6 = -1;
454 set_sa_port((struct sockaddr *)&sin6, htons(port));
456 s6 = setup_a_tcp_listener(data, (struct sockaddr *)&sin6);
457 if (s6 < 0)
458 return -1;
459 #ifdef IPV6_V6ONLY
460 if (setv6only(s6, 0))
461 com_err(data->prog, errno,
462 gettext("setsockopt(IPV6_V6ONLY,0) failed"));
463 #endif
465 s4 = setup_a_tcp_listener(data, (struct sockaddr *)&sin4);
466 #endif /* KRB5_USE_INET6 */
469 /* Sockets are created, prepare to listen on them. */
470 if (s4 >= 0) {
471 FD_SET(s4, &sstate.rfds);
472 if (s4 >= sstate.max)
473 sstate.max = s4 + 1;
474 if (add_tcp_listener_fd(data, s4) == 0)
475 close(s4);
476 else
477 krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s",
478 s4, paddr((struct sockaddr *)&sin4));
480 #ifdef KRB5_USE_INET6
481 if (s6 >= 0) {
482 FD_SET(s6, &sstate.rfds);
483 if (s6 >= sstate.max)
484 sstate.max = s6 + 1;
485 if (add_tcp_listener_fd(data, s6) == 0) {
486 close(s6);
487 s6 = -1;
488 } else
489 krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s",
490 s6, paddr((struct sockaddr *)&sin6));
491 if (s4 < 0)
492 krb5_klog_syslog(LOG_INFO,
493 "assuming IPv6 socket accepts IPv4");
495 #endif
497 return 0;
500 static int
501 setup_udp_port(void *P_data, struct sockaddr *addr)
503 struct socksetup *data = P_data;
504 int sock = -1, i;
505 char haddrbuf[NI_MAXHOST];
506 int err;
507 u_short port;
509 err = getnameinfo(addr, socklen(addr), haddrbuf, sizeof(haddrbuf),
510 0, 0, NI_NUMERICHOST);
511 if (err)
512 strcpy(haddrbuf, "<unprintable>");
514 switch (addr->sa_family) {
515 case AF_INET:
516 break;
517 #ifdef AF_INET6
518 case AF_INET6:
519 #ifdef KRB5_USE_INET6
520 break;
521 #else
523 static int first = 1;
524 if (first) {
525 krb5_klog_syslog (LOG_INFO, "skipping local ipv6 addresses");
526 first = 0;
528 return 0;
530 #endif
531 #endif
532 #ifdef AF_LINK /* some BSD systems, AIX */
533 case AF_LINK:
534 return 0;
535 #endif
536 #ifdef AF_DLI /* Direct Link Interface - DEC Ultrix/OSF1 link layer? */
537 case AF_DLI:
538 return 0;
539 #endif
540 default:
541 krb5_klog_syslog (LOG_INFO,
542 "skipping unrecognized local address family %d",
543 addr->sa_family);
544 return 0;
547 FOREACH_ELT (udp_port_data, i, port) {
548 sock = socket (addr->sa_family, SOCK_DGRAM, 0);
549 if (sock == -1) {
550 data->retval = errno;
551 com_err(data->prog, data->retval,
552 gettext("Cannot create server socket for port %d address %s"),
553 port, haddrbuf);
554 return 1;
556 set_sa_port(addr, htons(port));
557 if (bind (sock, (struct sockaddr *)addr, socklen (addr)) == -1) {
558 data->retval = errno;
559 com_err(data->prog, data->retval,
560 gettext("Cannot bind server socket to port %d address %s"),
561 port, haddrbuf);
562 return 1;
564 FD_SET (sock, &sstate.rfds);
565 if (sock >= sstate.max)
566 sstate.max = sock + 1;
567 krb5_klog_syslog (LOG_INFO, "listening on fd %d: udp %s", sock,
568 paddr((struct sockaddr *)addr));
569 if (add_udp_fd (data, sock) == 0)
570 return 1;
572 return 0;
575 #if 1
576 static void klog_handler(const void *data, size_t len)
578 static char buf[BUFSIZ];
579 static int bufoffset;
580 void *p;
582 #define flush_buf() \
583 (bufoffset \
584 ? (((buf[0] == 0 || buf[0] == '\n') \
585 ? (fork()==0?abort():(void)0) \
586 : (void)0), \
587 krb5_klog_syslog(LOG_INFO, "%s", buf), \
588 memset(buf, 0, sizeof(buf)), \
589 bufoffset = 0) \
590 : 0)
592 p = memchr(data, 0, len);
593 if (p)
594 len = (const char *)p - (const char *)data;
595 scan_for_newlines:
596 if (len == 0)
597 return;
598 p = memchr(data, '\n', len);
599 if (p) {
600 if (p != data)
601 klog_handler(data, (size_t)((const char *)p - (const char *)data));
602 flush_buf();
603 len -= ((const char *)p - (const char *)data) + 1;
604 data = 1 + (const char *)p;
605 goto scan_for_newlines;
606 } else if (len > sizeof(buf) - 1 || len + bufoffset > sizeof(buf) - 1) {
607 size_t x = sizeof(buf) - len - 1;
608 klog_handler(data, x);
609 flush_buf();
610 len -= x;
611 data = (const char *)data + x;
612 goto scan_for_newlines;
613 } else {
614 memcpy(buf + bufoffset, data, len);
615 bufoffset += len;
618 #endif
620 /* XXX */
621 extern int krb5int_debug_sendto_kdc;
622 extern void (*krb5int_sendtokdc_debug_handler)(const void*, size_t);
624 krb5_error_code
625 setup_network(const char *prog)
627 struct socksetup setup_data;
628 krb5_error_code retval;
629 char *cp;
630 int i, port;
632 FD_ZERO(&sstate.rfds);
633 FD_ZERO(&sstate.wfds);
634 FD_ZERO(&sstate.xfds);
635 sstate.max = 0;
637 /* krb5int_debug_sendto_kdc = 1; */
638 krb5int_sendtokdc_debug_handler = klog_handler;
640 /* Handle each realm's ports */
641 for (i=0; i<kdc_numrealms; i++) {
642 cp = kdc_realmlist[i]->realm_ports;
643 while (cp && *cp) {
644 if (*cp == ',' || isspace((int) *cp)) {
645 cp++;
646 continue;
648 port = strtol(cp, &cp, 10);
649 if (cp == 0)
650 break;
651 retval = add_udp_port(port);
652 if (retval)
653 return retval;
656 cp = kdc_realmlist[i]->realm_tcp_ports;
657 while (cp && *cp) {
658 if (*cp == ',' || isspace((int) *cp)) {
659 cp++;
660 continue;
662 port = strtol(cp, &cp, 10);
663 if (cp == 0)
664 break;
665 retval = add_tcp_port(port);
666 if (retval)
667 return retval;
671 setup_data.prog = prog;
672 setup_data.retval = 0;
673 krb5_klog_syslog (LOG_INFO, "setting up network...");
674 /* To do: Use RFC 2292 interface (or follow-on) and IPV6_PKTINFO,
675 so we might need only one UDP socket; fall back to binding
676 sockets on each address only if IPV6_PKTINFO isn't
677 supported. */
678 if (foreach_localaddr (&setup_data, setup_udp_port, 0, 0)) {
679 return setup_data.retval;
681 setup_tcp_listener_ports(&setup_data);
682 krb5_klog_syslog (LOG_INFO, "set up %d sockets", n_sockets);
683 if (n_sockets == 0) {
684 com_err(prog, 0, gettext("no sockets set up?"));
685 exit (1);
688 return 0;
691 static void init_addr(krb5_fulladdr *faddr, struct sockaddr *sa)
693 switch (sa->sa_family) {
694 case AF_INET:
695 faddr->address->addrtype = ADDRTYPE_INET;
696 faddr->address->length = IPV4_ADDR_LEN;
697 faddr->address->contents = (krb5_octet *) &sa2sin(sa)->sin_addr;
698 faddr->port = ntohs(sa2sin(sa)->sin_port);
699 break;
700 #ifdef KRB5_USE_INET6
701 case AF_INET6:
702 if (IN6_IS_ADDR_V4MAPPED(&sa2sin6(sa)->sin6_addr)) {
703 faddr->address->addrtype = ADDRTYPE_INET;
704 faddr->address->length = IPV4_ADDR_LEN;
705 /* offset to RAM address of ipv4 part of ipv6 address */
706 faddr->address->contents = (IPV6_ADDR_LEN - IPV4_ADDR_LEN) +
707 (krb5_octet *) &sa2sin6(sa)->sin6_addr;
708 } else {
709 faddr->address->addrtype = ADDRTYPE_INET6;
710 faddr->address->length = IPV6_ADDR_LEN;
711 faddr->address->contents = (krb5_octet *) &sa2sin6(sa)->sin6_addr;
713 faddr->port = ntohs(sa2sin6(sa)->sin6_port);
714 break;
715 #endif
716 default:
717 faddr->address->addrtype = -1;
718 faddr->address->length = 0;
719 faddr->address->contents = 0;
720 faddr->port = 0;
721 break;
725 static void process_packet(struct connection *conn, const char *prog,
726 int selflags)
728 int cc;
729 socklen_t saddr_len;
730 krb5_fulladdr faddr;
731 krb5_error_code retval;
732 struct sockaddr_storage saddr;
733 krb5_address addr;
734 krb5_data request;
735 krb5_data *response;
736 char pktbuf[MAX_DGRAM_SIZE];
737 int port_fd = conn->fd;
739 response = NULL;
740 saddr_len = sizeof(saddr);
741 cc = recvfrom(port_fd, pktbuf, sizeof(pktbuf), 0,
742 (struct sockaddr *)&saddr, &saddr_len);
743 if (cc == -1) {
744 if (errno != EINTR
745 /* This is how Linux indicates that a previous
746 transmission was refused, e.g., if the client timed out
747 before getting the response packet. */
748 && errno != ECONNREFUSED
750 com_err(prog, errno, gettext("while receiving from network"));
751 return;
753 if (!cc)
754 return; /* zero-length packet? */
756 request.length = cc;
757 request.data = pktbuf;
758 faddr.address = &addr;
759 init_addr(&faddr, ss2sa(&saddr));
760 /* this address is in net order */
761 if ((retval = dispatch(&request, &faddr, &response))) {
762 com_err(prog, retval, gettext("while dispatching (udp)"));
763 return;
765 cc = sendto(port_fd, response->data, (socklen_t) response->length, 0,
766 (struct sockaddr *)&saddr, saddr_len);
767 if (cc == -1) {
768 char addrbuf[46];
769 krb5_free_data(kdc_context, response);
770 if (inet_ntop(((struct sockaddr *)&saddr)->sa_family,
771 addr.contents, addrbuf, sizeof(addrbuf)) == 0) {
772 strcpy(addrbuf, "?");
774 com_err(prog, errno, gettext("while sending reply to %s/%d"),
775 addrbuf, faddr.port);
776 return;
778 if (cc != response->length) {
779 krb5_free_data(kdc_context, response);
780 com_err(prog, 0, gettext("short reply write %d vs %d\n"),
781 response->length, cc);
782 return;
784 krb5_free_data(kdc_context, response);
785 return;
788 static int tcp_data_counter;
789 /* Solaris kerberos: getting this value from elsewhere */
790 extern int max_tcp_data_connections;
792 static void kill_tcp_connection(struct connection *);
794 static void accept_tcp_connection(struct connection *conn, const char *prog,
795 int selflags)
797 int s;
798 struct sockaddr_storage addr_s;
799 struct sockaddr *addr = (struct sockaddr *)&addr_s;
800 socklen_t addrlen = sizeof(addr_s);
801 struct socksetup sockdata;
802 struct connection *newconn;
803 char tmpbuf[10];
805 s = accept(conn->fd, addr, &addrlen);
806 if (s < 0)
807 return;
808 setnbio(s), setnolinger(s);
810 sockdata.prog = prog;
811 sockdata.retval = 0;
813 newconn = add_tcp_data_fd(&sockdata, s);
814 if (newconn == 0)
815 return;
817 if (getnameinfo((struct sockaddr *)&addr_s, addrlen,
818 newconn->u.tcp.addrbuf, sizeof(newconn->u.tcp.addrbuf),
819 tmpbuf, sizeof(tmpbuf),
820 NI_NUMERICHOST | NI_NUMERICSERV))
821 strcpy(newconn->u.tcp.addrbuf, "???");
822 else {
823 char *p, *end;
824 p = newconn->u.tcp.addrbuf;
825 end = p + sizeof(newconn->u.tcp.addrbuf);
826 p += strlen(p);
827 if (end - p > 2 + strlen(tmpbuf)) {
828 *p++ = '.';
829 strcpy(p, tmpbuf);
832 #if 0
833 krb5_klog_syslog(LOG_INFO, "accepted TCP connection on socket %d from %s",
834 s, newconn->u.tcp.addrbuf);
835 #endif
837 newconn->u.tcp.addr_s = addr_s;
838 newconn->u.tcp.addrlen = addrlen;
839 newconn->u.tcp.bufsiz = 1024 * 1024;
840 newconn->u.tcp.buffer = malloc(newconn->u.tcp.bufsiz);
841 newconn->u.tcp.start_time = time(0);
843 if (++tcp_data_counter > max_tcp_data_connections) {
844 struct connection *oldest_tcp = NULL;
845 struct connection *c;
846 int i;
848 krb5_klog_syslog(LOG_INFO, "too many connections");
850 FOREACH_ELT (connections, i, c) {
851 if (c->type != CONN_TCP)
852 continue;
853 if (c == newconn)
854 continue;
855 #if 0
856 krb5_klog_syslog(LOG_INFO, "fd %d started at %ld", c->fd,
857 c->u.tcp.start_time);
858 #endif
859 if (oldest_tcp == NULL
860 || oldest_tcp->u.tcp.start_time > c->u.tcp.start_time)
861 oldest_tcp = c;
863 if (oldest_tcp != NULL) {
864 krb5_klog_syslog(LOG_INFO, "dropping tcp fd %d from %s",
865 oldest_tcp->fd, oldest_tcp->u.tcp.addrbuf);
866 kill_tcp_connection(oldest_tcp);
867 oldest_tcp = NULL;
870 if (newconn->u.tcp.buffer == 0) {
871 com_err(prog, errno, gettext("allocating buffer for new TCP session from %s"),
872 newconn->u.tcp.addrbuf);
873 delete_fd(newconn);
874 close(s);
875 tcp_data_counter--;
876 return;
878 newconn->u.tcp.offset = 0;
879 newconn->u.tcp.faddr.address = &newconn->u.tcp.kaddr;
880 init_addr(&newconn->u.tcp.faddr, ss2sa(&newconn->u.tcp.addr_s));
881 SG_SET(&newconn->u.tcp.sgbuf[0], newconn->u.tcp.lenbuf, 4);
882 SG_SET(&newconn->u.tcp.sgbuf[1], 0, 0);
884 FD_SET(s, &sstate.rfds);
885 if (sstate.max <= s)
886 sstate.max = s + 1;
889 static void
890 kill_tcp_connection(struct connection *conn)
892 if (conn->u.tcp.response)
893 krb5_free_data(kdc_context, conn->u.tcp.response);
894 free(conn->u.tcp.buffer);
895 FD_CLR(conn->fd, &sstate.rfds);
896 FD_CLR(conn->fd, &sstate.wfds);
897 if (sstate.max == conn->fd + 1)
898 while (sstate.max > 0
899 && ! FD_ISSET(sstate.max-1, &sstate.rfds)
900 && ! FD_ISSET(sstate.max-1, &sstate.wfds)
901 /* && ! FD_ISSET(sstate.max-1, &sstate.xfds) */
903 sstate.max--;
904 close(conn->fd);
905 conn->fd = -1;
906 delete_fd(conn);
907 tcp_data_counter--;
910 static krb5_error_code
911 make_toolong_error (krb5_data **out)
913 krb5_error errpkt;
914 krb5_error_code retval;
915 krb5_data *scratch;
917 retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec);
918 if (retval)
919 return retval;
920 errpkt.error = KRB_ERR_FIELD_TOOLONG;
921 errpkt.server = tgs_server;
922 errpkt.client = NULL;
923 errpkt.cusec = 0;
924 errpkt.ctime = 0;
925 errpkt.text.length = 0;
926 errpkt.text.data = 0;
927 errpkt.e_data.length = 0;
928 errpkt.e_data.data = 0;
929 scratch = malloc(sizeof(*scratch));
930 if (scratch == NULL)
931 return ENOMEM;
932 retval = krb5_mk_error(kdc_context, &errpkt, scratch);
933 if (retval) {
934 free(scratch);
935 return retval;
938 *out = scratch;
939 return 0;
942 static void
943 process_tcp_connection(struct connection *conn, const char *prog, int selflags)
945 if (selflags & SSF_WRITE) {
946 ssize_t nwrote;
947 SOCKET_WRITEV_TEMP tmp;
949 nwrote = SOCKET_WRITEV(conn->fd, conn->u.tcp.sgp, conn->u.tcp.sgnum,
950 tmp);
951 if (nwrote < 0) {
952 goto kill_tcp_connection;
954 if (nwrote == 0)
955 /* eof */
956 goto kill_tcp_connection;
957 while (nwrote) {
958 sg_buf *sgp = conn->u.tcp.sgp;
959 if (nwrote < SG_LEN(sgp)) {
960 SG_ADVANCE(sgp, nwrote);
961 nwrote = 0;
962 } else {
963 nwrote -= SG_LEN(sgp);
964 conn->u.tcp.sgp++;
965 conn->u.tcp.sgnum--;
966 if (conn->u.tcp.sgnum == 0 && nwrote != 0)
967 abort();
970 if (conn->u.tcp.sgnum == 0) {
971 /* finished sending */
972 /* We should go back to reading, though if we sent a
973 FIELD_TOOLONG error in reply to a length with the high
974 bit set, RFC 4120 says we have to close the TCP
975 stream. */
976 goto kill_tcp_connection;
978 } else if (selflags & SSF_READ) {
979 /* Read message length and data into one big buffer, already
980 allocated at connect time. If we have a complete message,
981 we stop reading, so we should only be here if there is no
982 data in the buffer, or only an incomplete message. */
983 size_t len;
984 ssize_t nread;
985 if (conn->u.tcp.offset < 4) {
986 /* msglen has not been computed */
987 /* XXX Doing at least two reads here, letting the kernel
988 worry about buffering. It'll be faster when we add
989 code to manage the buffer here. */
990 len = 4 - conn->u.tcp.offset;
991 nread = SOCKET_READ(conn->fd,
992 conn->u.tcp.buffer + conn->u.tcp.offset, len);
993 if (nread < 0)
994 /* error */
995 goto kill_tcp_connection;
996 if (nread == 0)
997 /* eof */
998 goto kill_tcp_connection;
999 conn->u.tcp.offset += nread;
1000 if (conn->u.tcp.offset == 4) {
1001 unsigned char *p = (unsigned char *)conn->u.tcp.buffer;
1002 conn->u.tcp.msglen = ((p[0] << 24)
1003 | (p[1] << 16)
1004 | (p[2] << 8)
1005 | p[3]);
1006 if (conn->u.tcp.msglen > conn->u.tcp.bufsiz - 4) {
1007 krb5_error_code err;
1008 /* message too big */
1009 krb5_klog_syslog(LOG_ERR, "TCP client %s wants %lu bytes, cap is %lu",
1010 conn->u.tcp.addrbuf, (unsigned long) conn->u.tcp.msglen,
1011 (unsigned long) conn->u.tcp.bufsiz - 4);
1012 /* XXX Should return an error. */
1013 err = make_toolong_error (&conn->u.tcp.response);
1014 if (err) {
1015 krb5_klog_syslog(LOG_ERR,
1016 "error constructing KRB_ERR_FIELD_TOOLONG error! %s",
1017 error_message(err));
1018 goto kill_tcp_connection;
1020 goto have_response;
1023 } else {
1024 /* msglen known */
1025 krb5_data request;
1026 krb5_error_code err;
1028 len = conn->u.tcp.msglen - (conn->u.tcp.offset - 4);
1029 nread = SOCKET_READ(conn->fd,
1030 conn->u.tcp.buffer + conn->u.tcp.offset, len);
1031 if (nread < 0)
1032 /* error */
1033 goto kill_tcp_connection;
1034 if (nread == 0)
1035 /* eof */
1036 goto kill_tcp_connection;
1037 conn->u.tcp.offset += nread;
1038 if (conn->u.tcp.offset < conn->u.tcp.msglen + 4)
1039 return;
1040 /* have a complete message, and exactly one message */
1041 request.length = conn->u.tcp.msglen;
1042 request.data = conn->u.tcp.buffer + 4;
1043 err = dispatch(&request, &conn->u.tcp.faddr,
1044 &conn->u.tcp.response);
1045 if (err) {
1046 com_err(prog, err, gettext("while dispatching (tcp)"));
1047 goto kill_tcp_connection;
1049 have_response:
1050 conn->u.tcp.lenbuf[0] = 0xff & (conn->u.tcp.response->length >> 24);
1051 conn->u.tcp.lenbuf[1] = 0xff & (conn->u.tcp.response->length >> 16);
1052 conn->u.tcp.lenbuf[2] = 0xff & (conn->u.tcp.response->length >> 8);
1053 conn->u.tcp.lenbuf[3] = 0xff & (conn->u.tcp.response->length >> 0);
1054 SG_SET(&conn->u.tcp.sgbuf[1], conn->u.tcp.response->data,
1055 conn->u.tcp.response->length);
1056 conn->u.tcp.sgp = conn->u.tcp.sgbuf;
1057 conn->u.tcp.sgnum = 2;
1058 FD_CLR(conn->fd, &sstate.rfds);
1059 FD_SET(conn->fd, &sstate.wfds);
1061 } else
1062 abort();
1064 return;
1066 kill_tcp_connection:
1067 kill_tcp_connection(conn);
1070 static void service_conn(struct connection *conn, const char *prog,
1071 int selflags)
1073 conn->service(conn, prog, selflags);
1076 krb5_error_code
1077 listen_and_process(const char *prog)
1079 int nfound;
1080 /* This struct contains 3 fd_set objects; on some platforms, they
1081 can be rather large. Making this static avoids putting all
1082 that junk on the stack. */
1083 static struct select_state sout;
1084 int i, sret;
1085 krb5_error_code err;
1087 if (conns == (struct connection **) NULL)
1088 return KDC5_NONET;
1090 while (!signal_requests_exit) {
1091 if (signal_requests_hup) {
1092 krb5_klog_reopen(kdc_context);
1093 signal_requests_hup = 0;
1095 sstate.end_time.tv_sec = sstate.end_time.tv_usec = 0;
1096 err = krb5int_cm_call_select(&sstate, &sout, &sret);
1097 if (err) {
1098 com_err(prog, err, gettext("while selecting for network input(1)"));
1099 continue;
1101 if (sret == -1) {
1102 if (errno != EINTR)
1103 com_err(prog, errno, gettext("while selecting for network input(2)"));
1104 continue;
1106 nfound = sret;
1107 for (i=0; i<n_sockets && nfound > 0; i++) {
1108 int sflags = 0;
1109 if (conns[i]->fd < 0)
1110 abort();
1111 if (FD_ISSET(conns[i]->fd, &sout.rfds))
1112 sflags |= SSF_READ, nfound--;
1113 if (FD_ISSET(conns[i]->fd, &sout.wfds))
1114 sflags |= SSF_WRITE, nfound--;
1115 if (sflags)
1116 service_conn(conns[i], prog, sflags);
1119 return 0;
1122 krb5_error_code
1123 closedown_network(const char *prog)
1125 int i;
1126 struct connection *conn;
1128 if (conns == (struct connection **) NULL)
1129 return KDC5_NONET;
1131 FOREACH_ELT (connections, i, conn) {
1132 if (conn->fd >= 0)
1133 (void) close(conn->fd);
1134 DEL (connections, i);
1135 /* There may also be per-connection data in the tcp structure
1136 (tcp.buffer, tcp.response) that we're not freeing here.
1137 That should only happen if we quit with a connection in
1138 progress. */
1139 free(conn);
1141 FREE_SET_DATA(connections);
1142 FREE_SET_DATA(udp_port_data);
1143 FREE_SET_DATA(tcp_port_data);
1145 return 0;
1148 #endif /* INET */