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