2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
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.
39 #include "adm_proto.h"
40 #include <sys/ioctl.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>
60 #include <sys/select.h>
62 #include <arpa/inet.h>
66 #ifndef ARPHRD_ETHER /* OpenBSD breaks on multiple inclusions */
70 #ifdef HAVE_SYS_FILIO_H
71 #include <sys/filio.h> /* FIONBIO */
74 #include "fake-addrinfo.h"
76 /* Misc utility routines. */
78 set_sa_port(struct sockaddr
*addr
, int port
)
80 switch (addr
->sa_family
) {
82 sa2sin(addr
)->sin_port
= port
;
86 sa2sin6(addr
)->sin6_port
= port
;
94 static int ipv6_enabled()
97 static int result
= -1;
100 s
= socket(AF_INET6
, SOCK_STREAM
, 0);
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)
121 setv6only(int sock
, int value
)
123 return setsockopt(sock
, IPPROTO_IPV6
, IPV6_V6ONLY
, &value
, sizeof(value
));
128 static const char *paddr (struct sockaddr
*sa
)
130 static char buf
[100];
132 if (getnameinfo(sa
, socklen(sa
),
133 buf
, sizeof(buf
), portbuf
, sizeof(portbuf
),
134 NI_NUMERICHOST
|NI_NUMERICSERV
))
135 strcpy(buf
, "<unprintable>");
137 unsigned int len
= sizeof(buf
) - strlen(buf
);
138 char *p
= buf
+ strlen(buf
);
139 if (len
> 2+strlen(portbuf
)) {
142 strncpy(p
, portbuf
, len
);
150 enum kdc_conn_type
{ CONN_UDP
, CONN_TCP_LISTENER
, CONN_TCP
};
152 /* Per-connection info. */
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 */
160 /* Type-specific information. */
169 struct sockaddr_storage addr_s
;
181 unsigned char lenbuf
[4];
185 /* crude denial-of-service avoidance support */
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))) \
206 : ((tmpptr = realloc(set.data, \
207 (int)(set.max + incr) * sizeof(set.data[0]))) \
208 ? (set.data = tmpptr, set.max += incr, 1) \
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) \
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
;
234 static struct select_state sstate
;
236 static krb5_error_code
add_udp_port(int port
)
241 u_short s_port
= port
;
246 FOREACH_ELT (udp_port_data
, i
, val
)
249 if (!ADD(udp_port_data
, s_port
, tmp
))
254 static krb5_error_code
add_tcp_port(int port
)
259 u_short s_port
= port
;
264 FOREACH_ELT (tcp_port_data
, i
, val
)
267 if (!ADD(tcp_port_data
, s_port
, tmp
))
273 #define USE_AF AF_INET
274 #define USE_TYPE SOCK_DGRAM
276 #define SOCKET_ERRNO errno
277 #include "foreachaddr.h"
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
;
291 newconn
= malloc(sizeof(*newconn
));
293 data
->retval
= errno
;
294 com_err(data
->prog
, errno
,
295 gettext("cannot allocate storage for connection info"));
298 if (!ADD(connections
, newconn
, tmp
)) {
299 data
->retval
= errno
;
300 com_err(data
->prog
, data
->retval
, gettext("cannot save socket info"));
305 memset(newconn
, 0, sizeof(*newconn
));
306 newconn
->type
= conntype
;
308 newconn
->service
= service
;
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
);
335 delete_fd (struct connection
*xconn
)
337 struct connection
*conn
;
340 FOREACH_ELT(connections
, i
, conn
)
351 static const int one
= 1;
352 return ioctlsocket(sock
, FIONBIO
, (const void *)&one
);
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. */
364 setup_a_tcp_listener(struct socksetup
*data
, struct sockaddr
*addr
)
368 sock
= socket(addr
->sa_family
, SOCK_STREAM
, 0);
370 com_err(data
->prog
, errno
,
371 gettext("Cannot create TCP server socket on %s"),
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"));
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
));
391 if (listen(sock
, 5) < 0) {
392 com_err(data
->prog
, errno
,
393 gettext("Cannot listen on TCP server socket on %s"),
399 com_err(data
->prog
, errno
,
400 gettext("cannot set listening tcp socket on %s non-blocking"),
405 if (setnolinger(sock
)) {
406 com_err(data
->prog
, errno
,
407 gettext("disabling SO_LINGER on TCP socket on %s"),
416 setup_tcp_listener_ports(struct socksetup
*data
)
418 struct sockaddr_in sin4
;
419 #ifdef KRB5_USE_INET6
420 struct sockaddr_in6 sin6
;
424 memset(&sin4
, 0, sizeof(sin4
));
425 sin4
.sin_family
= AF_INET
;
427 sin4
.sin_len
= sizeof(sin4
);
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
;
435 sin6
.sin6_len
= sizeof(sin6
);
437 sin6
.sin6_addr
= in6addr_any
;
440 FOREACH_ELT (tcp_port_data
, i
, port
) {
443 set_sa_port((struct sockaddr
*)&sin4
, htons(port
));
444 if (!ipv6_enabled()) {
445 s4
= setup_a_tcp_listener(data
, (struct sockaddr
*)&sin4
);
450 #ifndef KRB5_USE_INET6
455 set_sa_port((struct sockaddr
*)&sin6
, htons(port
));
457 s6
= setup_a_tcp_listener(data
, (struct sockaddr
*)&sin6
);
461 if (setv6only(s6
, 0))
462 com_err(data
->prog
, errno
,
463 gettext("setsockopt(IPV6_V6ONLY,0) failed"));
466 s4
= setup_a_tcp_listener(data
, (struct sockaddr
*)&sin4
);
467 #endif /* KRB5_USE_INET6 */
470 /* Sockets are created, prepare to listen on them. */
472 FD_SET(s4
, &sstate
.rfds
);
473 if (s4
>= sstate
.max
)
475 if (add_tcp_listener_fd(data
, s4
) == 0)
478 krb5_klog_syslog(LOG_INFO
, "listening on fd %d: tcp %s",
479 s4
, paddr((struct sockaddr
*)&sin4
));
481 #ifdef KRB5_USE_INET6
483 FD_SET(s6
, &sstate
.rfds
);
484 if (s6
>= sstate
.max
)
486 if (add_tcp_listener_fd(data
, s6
) == 0) {
490 krb5_klog_syslog(LOG_INFO
, "listening on fd %d: tcp %s",
491 s6
, paddr((struct sockaddr
*)&sin6
));
493 krb5_klog_syslog(LOG_INFO
,
494 "assuming IPv6 socket accepts IPv4");
502 setup_udp_port(void *P_data
, struct sockaddr
*addr
)
504 struct socksetup
*data
= P_data
;
506 char haddrbuf
[NI_MAXHOST
];
510 err
= getnameinfo(addr
, socklen(addr
), haddrbuf
, sizeof(haddrbuf
),
511 0, 0, NI_NUMERICHOST
);
513 strcpy(haddrbuf
, "<unprintable>");
515 switch (addr
->sa_family
) {
520 #ifdef KRB5_USE_INET6
524 static int first
= 1;
526 krb5_klog_syslog (LOG_INFO
, "skipping local ipv6 addresses");
533 #ifdef AF_LINK /* some BSD systems, AIX */
537 #ifdef AF_DLI /* Direct Link Interface - DEC Ultrix/OSF1 link layer? */
542 krb5_klog_syslog (LOG_INFO
,
543 "skipping unrecognized local address family %d",
548 FOREACH_ELT (udp_port_data
, i
, port
) {
549 sock
= socket (addr
->sa_family
, SOCK_DGRAM
, 0);
551 data
->retval
= errno
;
552 com_err(data
->prog
, data
->retval
,
553 gettext("Cannot create server socket for port %d address %s"),
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"),
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)
577 static void klog_handler(const void *data
, size_t len
)
579 static char buf
[BUFSIZ
];
580 static int bufoffset
;
583 #define flush_buf() \
585 ? (((buf[0] == 0 || buf[0] == '\n') \
586 ? (fork()==0?abort():(void)0) \
588 krb5_klog_syslog(LOG_INFO, "%s", buf), \
589 memset(buf, 0, sizeof(buf)), \
593 p
= memchr(data
, 0, len
);
595 len
= (const char *)p
- (const char *)data
;
599 p
= memchr(data
, '\n', len
);
602 klog_handler(data
, (size_t)((const char *)p
- (const char *)data
));
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
);
612 data
= (const char *)data
+ x
;
613 goto scan_for_newlines
;
615 memcpy(buf
+ bufoffset
, data
, len
);
622 extern int krb5int_debug_sendto_kdc
;
623 extern void (*krb5int_sendtokdc_debug_handler
)(const void*, size_t);
626 setup_network(const char *prog
)
628 struct socksetup setup_data
;
629 krb5_error_code retval
;
633 FD_ZERO(&sstate
.rfds
);
634 FD_ZERO(&sstate
.wfds
);
635 FD_ZERO(&sstate
.xfds
);
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
;
645 if (*cp
== ',' || isspace((int) *cp
)) {
649 port
= strtol(cp
, &cp
, 10);
652 retval
= add_udp_port(port
);
657 cp
= kdc_realmlist
[i
]->realm_tcp_ports
;
659 if (*cp
== ',' || isspace((int) *cp
)) {
663 port
= strtol(cp
, &cp
, 10);
666 retval
= add_tcp_port(port
);
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
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?"));
692 static void init_addr(krb5_fulladdr
*faddr
, struct sockaddr
*sa
)
694 switch (sa
->sa_family
) {
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
);
701 #ifdef KRB5_USE_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
;
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
);
718 faddr
->address
->addrtype
= -1;
719 faddr
->address
->length
= 0;
720 faddr
->address
->contents
= 0;
726 static void process_packet(struct connection
*conn
, const char *prog
,
732 krb5_error_code retval
;
733 struct sockaddr_storage saddr
;
737 char pktbuf
[MAX_DGRAM_SIZE
];
738 int port_fd
= conn
->fd
;
741 saddr_len
= sizeof(saddr
);
742 cc
= recvfrom(port_fd
, pktbuf
, sizeof(pktbuf
), 0,
743 (struct sockaddr
*)&saddr
, &saddr_len
);
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"));
755 return; /* zero-length packet? */
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)"));
766 cc
= sendto(port_fd
, response
->data
, (socklen_t
) response
->length
, 0,
767 (struct sockaddr
*)&saddr
, saddr_len
);
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
);
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
);
785 krb5_free_data(kdc_context
, response
);
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
,
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
;
806 s
= accept(conn
->fd
, addr
, &addrlen
);
809 setnbio(s
), setnolinger(s
);
811 sockdata
.prog
= prog
;
814 newconn
= add_tcp_data_fd(&sockdata
, s
);
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
, "???");
825 p
= newconn
->u
.tcp
.addrbuf
;
826 end
= p
+ sizeof(newconn
->u
.tcp
.addrbuf
);
828 if (end
- p
> 2 + strlen(tmpbuf
)) {
834 krb5_klog_syslog(LOG_INFO
, "accepted TCP connection on socket %d from %s",
835 s
, newconn
->u
.tcp
.addrbuf
);
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
;
849 krb5_klog_syslog(LOG_INFO
, "too many connections");
851 FOREACH_ELT (connections
, i
, c
) {
852 if (c
->type
!= CONN_TCP
)
857 krb5_klog_syslog(LOG_INFO
, "fd %d started at %ld", c
->fd
,
858 c
->u
.tcp
.start_time
);
860 if (oldest_tcp
== NULL
861 || oldest_tcp
->u
.tcp
.start_time
> c
->u
.tcp
.start_time
)
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
);
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
);
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
);
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) */
912 static krb5_error_code
913 make_toolong_error (krb5_data
**out
)
916 krb5_error_code retval
;
919 retval
= krb5_us_timeofday(kdc_context
, &errpkt
.stime
, &errpkt
.susec
);
922 errpkt
.error
= KRB_ERR_FIELD_TOOLONG
;
923 errpkt
.server
= tgs_server
;
924 errpkt
.client
= NULL
;
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
));
934 retval
= krb5_mk_error(kdc_context
, &errpkt
, scratch
);
945 process_tcp_connection(struct connection
*conn
, const char *prog
, int selflags
)
947 if (selflags
& SSF_WRITE
) {
949 SOCKET_WRITEV_TEMP tmp
;
951 nwrote
= SOCKET_WRITEV(conn
->fd
, conn
->u
.tcp
.sgp
, conn
->u
.tcp
.sgnum
,
954 goto kill_tcp_connection
;
958 goto kill_tcp_connection
;
960 sg_buf
*sgp
= conn
->u
.tcp
.sgp
;
961 if (nwrote
< SG_LEN(sgp
)) {
962 SG_ADVANCE(sgp
, nwrote
);
965 nwrote
-= SG_LEN(sgp
);
968 if (conn
->u
.tcp
.sgnum
== 0 && nwrote
!= 0)
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
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. */
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
);
997 goto kill_tcp_connection
;
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)
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
);
1017 krb5_klog_syslog(LOG_ERR
,
1018 "error constructing KRB_ERR_FIELD_TOOLONG error! %s",
1019 error_message(err
));
1020 goto kill_tcp_connection
;
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
);
1035 goto kill_tcp_connection
;
1038 goto kill_tcp_connection
;
1039 conn
->u
.tcp
.offset
+= nread
;
1040 if (conn
->u
.tcp
.offset
< conn
->u
.tcp
.msglen
+ 4)
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
);
1048 com_err(prog
, err
, gettext("while dispatching (tcp)"));
1049 goto kill_tcp_connection
;
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
);
1068 kill_tcp_connection
:
1069 kill_tcp_connection(conn
);
1072 static void service_conn(struct connection
*conn
, const char *prog
,
1075 conn
->service(conn
, prog
, selflags
);
1079 listen_and_process(const char *prog
)
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
;
1087 krb5_error_code err
;
1089 if (conns
== (struct connection
**) NULL
)
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
);
1100 com_err(prog
, err
, gettext("while selecting for network input(1)"));
1105 com_err(prog
, errno
, gettext("while selecting for network input(2)"));
1109 for (i
=0; i
<n_sockets
&& nfound
> 0; i
++) {
1111 if (conns
[i
]->fd
< 0)
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
--;
1118 service_conn(conns
[i
], prog
, sflags
);
1125 closedown_network(const char *prog
)
1128 struct connection
*conn
;
1130 if (conns
== (struct connection
**) NULL
)
1133 FOREACH_ELT (connections
, i
, conn
) {
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
1143 FREE_SET_DATA(connections
);
1144 FREE_SET_DATA(udp_port_data
);
1145 FREE_SET_DATA(tcp_port_data
);