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