1/* 2 * $Id: dsi_tcp.c,v 1.25 2009-12-08 22:34:37 didg Exp $ 3 * 4 * Copyright (c) 1997, 1998 Adrian Sun (asun@zoology.washington.edu) 5 * All rights reserved. See COPYRIGHT. 6 * 7 * this provides both proto_open() and proto_close() to account for 8 * protocol specific initialization and shutdown procedures. all the 9 * read/write stuff is done in dsi_stream.c. */ 10 11#ifdef HAVE_CONFIG_H 12#include "config.h" 13#endif /* HAVE_CONFIG_H */ 14 15#include <stdio.h> 16#include <stdlib.h> 17#include <string.h> 18#ifdef HAVE_UNISTD_H 19#include <unistd.h> 20#endif /* HAVE_UNISTD_H */ 21#include <errno.h> 22#ifdef HAVE_NETDB_H 23#include <netdb.h> 24#endif /* HAVE_NETDB_H */ 25#include <sys/types.h> 26#include <sys/time.h> 27#include <sys/socket.h> 28 29#ifdef HAVE_STDINT_H 30#include <stdint.h> 31#endif /* HAVE_STDINT_H */ 32 33#include <sys/ioctl.h> 34#ifdef TRU64 35#include <sys/mbuf.h> 36#include <net/route.h> 37#endif /* TRU64 */ 38#include <net/if.h> 39#include <netinet/tcp.h> 40#include <netinet/in.h> 41#include <arpa/inet.h> 42 43#include <signal.h> 44#include <atalk/logger.h> 45 46#ifdef __svr4__ 47#include <sys/sockio.h> 48#endif /* __svr4__ */ 49 50#ifdef TCPWRAP 51#include <tcpd.h> 52int allow_severity = log_info; 53int deny_severity = log_warning; 54#endif /* TCPWRAP */ 55 56#include <atalk/dsi.h> 57#include <atalk/compat.h> 58#include <atalk/util.h> 59#include <netatalk/endian.h> 60#include "dsi_private.h" 61 62#define min(a,b) ((a) < (b) ? (a) : (b)) 63 64#ifndef DSI_TCPMAXPEND 65#define DSI_TCPMAXPEND 20 /* max # of pending connections */ 66#endif /* DSI_TCPMAXPEND */ 67 68#ifndef DSI_TCPTIMEOUT 69#define DSI_TCPTIMEOUT 120 /* timeout in seconds for connections */ 70#endif /* ! DSI_TCPTIMEOUT */ 71 72 73/* FIXME/SOCKLEN_T: socklen_t is a unix98 feature. */ 74#ifndef SOCKLEN_T 75#define SOCKLEN_T unsigned int 76#endif /* ! SOCKLEN_T */ 77 78static void dsi_tcp_close(DSI *dsi) 79{ 80 if (dsi->socket == -1) 81 return; 82 83 close(dsi->socket); 84 dsi->socket = -1; 85} 86 87/* alarm handler for tcp_open */ 88static void timeout_handler(int sig _U_) 89{ 90 LOG(log_error, logtype_dsi, "dsi_tcp_open: connection timed out"); 91 exit(EXITERR_CLNT); 92} 93 94static struct itimerval itimer; 95/* accept the socket and do a little sanity checking */ 96static int dsi_tcp_open(DSI *dsi) 97{ 98 pid_t pid; 99 SOCKLEN_T len; 100 101 len = sizeof(dsi->client); 102 dsi->socket = accept(dsi->serversock, (struct sockaddr *) &dsi->client, &len); 103 104#ifdef TCPWRAP 105 { 106 struct request_info req; 107 request_init(&req, RQ_DAEMON, dsi->program, RQ_FILE, dsi->socket, NULL); 108 fromhost(&req); 109 if (!hosts_access(&req)) { 110 LOG(deny_severity, logtype_dsi, "refused connect from %s", eval_client(&req)); 111 close(dsi->socket); 112 errno = ECONNREFUSED; 113 dsi->socket = -1; 114 } 115 } 116#endif /* TCPWRAP */ 117 118 if (dsi->socket < 0) 119 return -1; 120 121 getitimer(ITIMER_PROF, &itimer); 122 if (0 == (pid = fork()) ) { /* child */ 123 static struct itimerval timer = {{0, 0}, {DSI_TCPTIMEOUT, 0}}; 124 struct sigaction newact, oldact; 125 u_int8_t block[DSI_BLOCKSIZ]; 126 size_t stored; 127 128 /* Immediateyl mark globally that we're a child now */ 129 parent_or_child = 1; 130 131 /* reset signals */ 132 server_reset_signal(); 133 134#ifndef DEBUGGING 135 /* install an alarm to deal with non-responsive connections */ 136 newact.sa_handler = timeout_handler; 137 sigemptyset(&newact.sa_mask); 138 newact.sa_flags = 0; 139 sigemptyset(&oldact.sa_mask); 140 oldact.sa_flags = 0; 141 setitimer(ITIMER_PROF, &itimer, NULL); 142 143 if ((sigaction(SIGALRM, &newact, &oldact) < 0) || 144 (setitimer(ITIMER_REAL, &timer, NULL) < 0)) { 145 LOG(log_error, logtype_dsi, "dsi_tcp_open: %s", strerror(errno)); 146 exit(EXITERR_SYS); 147 } 148#endif 149 150 /* read in commands. this is similar to dsi_receive except 151 * for the fact that we do some sanity checking to prevent 152 * delinquent connections from causing mischief. */ 153 154 /* read in the first two bytes */ 155 len = dsi_stream_read(dsi, block, 2); 156 if (!len ) { 157 /* connection already closed, don't log it (normal OSX 10.3 behaviour) */ 158 exit(EXITERR_CLNT); 159 } 160 if (len < 2 || (block[0] > DSIFL_MAX) || (block[1] > DSIFUNC_MAX)) { 161 LOG(log_error, logtype_dsi, "dsi_tcp_open: invalid header"); 162 exit(EXITERR_CLNT); 163 } 164 165 /* read in the rest of the header */ 166 stored = 2; 167 while (stored < DSI_BLOCKSIZ) { 168 len = dsi_stream_read(dsi, block + stored, sizeof(block) - stored); 169 if (len > 0) 170 stored += len; 171 else { 172 LOG(log_error, logtype_dsi, "dsi_tcp_open: stream_read: %s", strerror(errno)); 173 exit(EXITERR_CLNT); 174 } 175 } 176 177 dsi->header.dsi_flags = block[0]; 178 dsi->header.dsi_command = block[1]; 179 memcpy(&dsi->header.dsi_requestID, block + 2, 180 sizeof(dsi->header.dsi_requestID)); 181 memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code)); 182 memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len)); 183 memcpy(&dsi->header.dsi_reserved, block + 12, 184 sizeof(dsi->header.dsi_reserved)); 185 dsi->clientID = ntohs(dsi->header.dsi_requestID); 186 187 /* make sure we don't over-write our buffers. */ 188 dsi->cmdlen = min(ntohl(dsi->header.dsi_len), DSI_CMDSIZ); 189 190 stored = 0; 191 while (stored < dsi->cmdlen) { 192 len = dsi_stream_read(dsi, dsi->commands + stored, dsi->cmdlen - stored); 193 if (len > 0) 194 stored += len; 195 else { 196 LOG(log_error, logtype_dsi, "dsi_tcp_open: stream_read: %s", strerror(errno)); 197 exit(EXITERR_CLNT); 198 } 199 } 200 201 /* stop timer and restore signal handler */ 202#ifndef DEBUGGING 203 memset(&timer, 0, sizeof(timer)); 204 setitimer(ITIMER_REAL, &timer, NULL); 205 sigaction(SIGALRM, &oldact, NULL); 206#endif 207 208 LOG(log_info, logtype_dsi, "AFP/TCP session from %s:%u", 209 getip_string((struct sockaddr *)&dsi->client), 210 getip_port((struct sockaddr *)&dsi->client)); 211 } 212 213 /* send back our pid */ 214 return pid; 215} 216 217/* get it from the interface list */ 218#ifndef IFF_SLAVE 219#define IFF_SLAVE 0 220#endif 221 222static void guess_interface(DSI *dsi, const char *hostname, const char *port) 223{ 224 int fd; 225 char **start, **list; 226 struct ifreq ifr; 227 struct sockaddr_in *sa = (struct sockaddr_in *)&dsi->server; 228 229 start = list = getifacelist(); 230 if (!start) 231 return; 232 233 fd = socket(PF_INET, SOCK_STREAM, 0); 234 235 while (list && *list) { 236 strlcpy(ifr.ifr_name, *list, sizeof(ifr.ifr_name)); 237 list++; 238 239 240 if (ioctl(dsi->serversock, SIOCGIFFLAGS, &ifr) < 0) 241 continue; 242 243 if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_SLAVE)) 244 continue; 245 246 if (!(ifr.ifr_flags & (IFF_UP | IFF_RUNNING)) ) 247 continue; 248 249 if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) 250 continue; 251 252 memset(&dsi->server, 0, sizeof(struct sockaddr_storage)); 253 sa->sin_family = AF_INET; 254 sa->sin_port = htons(atoi(port)); 255 sa->sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; 256 257 LOG(log_info, logtype_dsi, "dsi_tcp: '%s:%s' on interface '%s' will be used instead.", 258 getip_string((struct sockaddr *)&dsi->server), port, ifr.ifr_name); 259 goto iflist_done; 260 } 261 LOG(log_info, logtype_dsi, "dsi_tcp (Chooser will not select afp/tcp) " 262 "Check to make sure %s is in /etc/hosts and the correct domain is in " 263 "/etc/resolv.conf: %s", hostname, strerror(errno)); 264 265iflist_done: 266 close(fd); 267 freeifacelist(start); 268} 269 270 271#ifndef AI_NUMERICSERV 272#define AI_NUMERICSERV 0 273#endif 274 275/* this needs to accept passed in addresses */ 276int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address, 277 const char *port, const int proxy) 278{ 279 int ret; 280 int flag; 281 struct addrinfo hints, *servinfo, *p; 282 283 dsi->protocol = DSI_TCPIP; 284 285 /* Prepare hint for getaddrinfo */ 286 memset(&hints, 0, sizeof hints); 287#if !defined(FREEBSD) 288 hints.ai_family = AF_UNSPEC; 289#endif 290 hints.ai_socktype = SOCK_STREAM; 291 hints.ai_flags = AI_NUMERICSERV; 292 293 if ( ! address) { 294 hints.ai_flags |= AI_PASSIVE; 295#if defined(FREEBSD) 296 hints.ai_family = AF_INET6; 297#endif 298 } else { 299 hints.ai_flags |= AI_NUMERICHOST; 300#if defined(FREEBSD) 301 hints.ai_family = AF_UNSPEC; 302#endif 303 } 304 if ((ret = getaddrinfo(address ? address : NULL, port ? port : "548", &hints, &servinfo)) != 0) { 305 LOG(log_error, logtype_dsi, "dsi_tcp_init: getaddrinfo: %s\n", gai_strerror(ret)); 306 return 0; 307 } 308 309 /* create a socket */ 310 if (proxy) 311 dsi->serversock = -1; 312 else { 313 /* loop through all the results and bind to the first we can */ 314 for (p = servinfo; p != NULL; p = p->ai_next) { 315 if ((dsi->serversock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { 316 LOG(log_info, logtype_dsi, "dsi_tcp_init: socket: %s", strerror(errno)); 317 continue; 318 } 319 320 /* 321 * Set some socket options: 322 * SO_REUSEADDR deals w/ quick close/opens 323 * TCP_NODELAY diables Nagle 324 */ 325#ifdef SO_REUSEADDR 326 flag = 1; 327 setsockopt(dsi->serversock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); 328#endif 329#if defined(FREEBSD) && defined(IPV6_BINDV6ONLY) 330 int on = 0; 331 setsockopt(dsi->serversock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&on, sizeof (on)); 332#endif 333 334#ifndef SOL_TCP 335#define SOL_TCP IPPROTO_TCP 336#endif 337 flag = 1; 338 setsockopt(dsi->serversock, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag)); 339 340 if (bind(dsi->serversock, p->ai_addr, p->ai_addrlen) == -1) { 341 close(dsi->serversock); 342 LOG(log_info, logtype_dsi, "dsi_tcp_init: bind: %s\n", strerror(errno)); 343 continue; 344 } 345 346 if (listen(dsi->serversock, DSI_TCPMAXPEND) < 0) { 347 close(dsi->serversock); 348 LOG(log_info, logtype_dsi, "dsi_tcp_init: listen: %s\n", strerror(errno)); 349 continue; 350 } 351 352 break; 353 } 354 355 if (p == NULL) { 356 LOG(log_error, logtype_dsi, "dsi_tcp_init: no suitable network config for TCP socket"); 357 freeaddrinfo(servinfo); 358 return 0; 359 } 360 361 /* Copy struct sockaddr to struct sockaddr_storage */ 362 memcpy(&dsi->server, p->ai_addr, p->ai_addrlen); 363 freeaddrinfo(servinfo); 364 } /* if (proxy) */ 365 366 /* Point protocol specific functions to tcp versions */ 367 dsi->proto_open = dsi_tcp_open; 368 dsi->proto_close = dsi_tcp_close; 369 370 /* get real address for GetStatus. */ 371 372 if (address) { 373 /* address is a parameter, use it 'as is' */ 374 return 1; 375 } 376 377 /* Prepare hint for getaddrinfo */ 378 memset(&hints, 0, sizeof hints); 379 hints.ai_family = AF_UNSPEC; 380 hints.ai_socktype = SOCK_STREAM; 381 382 if ((ret = getaddrinfo(hostname, port ? port : "548", &hints, &servinfo)) != 0) { 383 LOG(log_info, logtype_dsi, "dsi_tcp_init: getaddrinfo '%s': %s\n", hostname, gai_strerror(ret)); 384 goto interfaces; 385 } 386 387 for (p = servinfo; p != NULL; p = p->ai_next) { 388 if (p->ai_family == AF_INET) { // IPv4 389 struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; 390 if ( (ipv4->sin_addr.s_addr & htonl(0x7f000000)) != htonl(0x7f000000) ) 391 break; 392 } else { // IPv6 393 struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; 394 unsigned char ipv6loopb[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; 395 if ((memcmp(ipv6->sin6_addr.s6_addr, ipv6loopb, 16)) != 0) 396 break; 397 } 398 } 399 400 if (p) { 401 /* Store found address in dsi->server */ 402 memcpy(&dsi->server, p->ai_addr, p->ai_addrlen); 403 freeaddrinfo(servinfo); 404 return 1; 405 } 406 LOG(log_info, logtype_dsi, "dsi_tcp: hostname '%s' resolves to loopback address", hostname); 407 freeaddrinfo(servinfo); 408 409interfaces: 410 guess_interface(dsi, hostname, port ? port : "548"); 411 return 1; 412} 413 414