/* Net2file.c * Call me Net2file. I accept a connection from a Mark-5 machine and * write the received data to a file. I'll try to create this file if * necessary or append. You should start me first, then command * disc2net or in2net in the Mark-5 machine. Command-line options: * -f File name, default save.data * -r Socket rcvbufs, default 0 = OS default * -p Protocol, tcp or udp, default tcp * -d Data port, default m5data * -m Msglev, default 1 * -h Help * You can watch how I'm doing with an * ls -l filename * but this will lag the actual progress because of buffering. I will * end when the Mark-5 sending socket disconnects, or end me with ^C, * after which filename will be ready to use. Note that m5data tcp * or udp needs to be in /etc/services on this machine. Copy from a * Mark-5 machine if need be. This version of Net2file.c compiles * with gcc on Linux or HP-UX and also with cc -Ae on HP-UX. It won't * compile with g++ without some mods. This version now supports file * sizes larger than 2 Gbytes. Revised: 2005 May 14, JAB */ #define _LARGEFILE_SOURCE /* Large File Support (LFS) */ #define _LARGEFILE64_SOURCE #include #include /* For strrchr() */ #include /* For atoi() */ #include /* For open(), socket(), connect(), etc. */ #include /* For open(), fchmod(), etc. */ #include /* For fcntl() */ #include /* For open() and fcntl() */ #include /* For socket(), connect(), etc. */ #include /* For socket() with PF_INET, etc. */ #include /* For getservbyname() */ #define TRUE 1 #define FALSE 0 char * me; /* My name */ int msglev = 1; /* Debug level, default */ int main(int argc, char * argv[]) { /* Net2file */ int port, sock, sock2, k, len, type, tof, on; char * filename = "save.data"; /* File name default */ char * dataport = "m5data"; /* Data port default */ char * smod = "tcp"; /* Protocol default */ unsigned int rcvbuf, rcvbufs = 0; /* Receive buffer size */ /* (Don't confuse these two) */ socklen_t optlen; /* For getsockopt() */ FILE * file; struct servent * set; /* From getservbyname() */ struct sockaddr_in socaddin; /* For bind() socket info */ struct sockaddr_in socadd; /* For accept() socket info */ int kk; /* For accept() (should be socklen_t) */ unsigned long rbuff[131072]; /* For data */ /* *** Initialize *** */ me = (me = strrchr(argv[0], '/')) == NULL ? argv[0] : me+1; /* My name */ /* Parse command-line options, if any */ k = 0; while (k >= 0) { switch (k = getopt(argc, argv, "hf:r:p:d:m:")) { /* That's help, filename, rcvbufs, protocol, msglev */ case 'h' : /* Help */ (void) fprintf(stderr, "%s USAGE: \n %s -f -r -p

-d -m -h \n" " where is file name, default save.data, \n" " is socket rcvbufs, bytes, OS default, \n" "

is protocol, tcp or udp, default tcp, \n" " is data port, default m5data, \n" " is message level, default 1, \n" " and -h shows this help message. \n", me, me); return(0); /* End here */ break; /* (Not used) */ case 'm' : /* msglev */ /* msglev = -1 Vast quantities of debuggery prints * msglev = 0 Some debuggery prints * msglev = 1 Default and normal operation; warnings and errors print * msglev = 2 Only errors and operational messages print * msglev = 3 Nothing prints except fatal errors when program dies * (default 1 above) */ msglev = atoi(optarg); /* (No check) */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s DEBUG: msglev set to %d \n", me, msglev); break; case 'f' : /* File name */ filename = optarg; break; case 'r' : /* rcvbufs */ rcvbufs = atoi(optarg); /* Yes */ break; case 'p' : /* Protocol */ smod = optarg; /* Yes */ break; case 'd' : /* Data port */ dataport = optarg; break; case '?' : /* Error: character not in list */ if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s WARNING: Unknown option on command line \n", me); break; case ':' : /* Error: missing numerical parameter */ if (msglev < 2) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s WARNING: Missing number on command line \n", me); break; default : /* Probably -1, end of parameters */ break; } /* End of switch on k */ } /* End of while k */ /* ** Open or create output file ** */ if ((port = open64(filename, O_WRONLY | O_CREAT)) < 0) { /* OK? */ (void) fprintf(stderr, "%s ERROR: \007 Can't open() file ", me); /* Nope */ perror(filename); return(-1); /* Error */ } (void) fchmod(port, 0664); /* Ignore errors (e.g., owned by someone else) */ /* Open this output file also as a stream */ if ((file = fdopen(port, "a")) == NULL) { /* OK? */ (void) fprintf(stderr, "%s ERROR: \007 Can't fdopen() file ", me); /* No */ perror(filename); return(-2); /* Error */ } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s DEBUG: Opened file %s \n", me, filename); /* ** Create and configure a socket to receive data ** */ if (strcasecmp(smod, "tcp") == 0) /* tcp? */ type = SOCK_STREAM; /* Yes */ else if (strcasecmp(smod, "udp") == 0) /* udp? */ type = SOCK_DGRAM; /* Yes */ else { /* Neither tcp nor udp is an error */ (void) fprintf(stderr, /* Yes */ "%s ERROR: Can't do type %s \n", me, smod); return(-3); /* Error */ } if ((sock = socket(PF_INET, type, 0)) < 0) { /* OK? */ (void) fprintf(stderr, /* Nope */ "%s ERROR: \007 socket() returned %d ", me, sock); perror("error"); return(-4); /* Error */ } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s DEBUG: Opened %s socket %d \n", me, smod, sock); /* If tcp, then we set keep-alive in case a firewall needs it */ if (type == SOCK_STREAM) { /* tcp? */ tof = TRUE; /* Yes */ if(setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &tof, sizeof(int)) < 0) { /* Set keep-alive, OK? */ (void) fprintf(stderr, /* Nope */ "%s ERROR: \007 setsockopt() SO_KEEPALIVE returned ", me); perror("error"); return(-5); /* Error */ } } /* SO_RCVBUF sets or gets the maximum socket receive buffer in bytes. * The default value is set by the rmem_default sysctl, and the * maximum allowed value is set by the rmem_max sysctl. */ if (rcvbufs > 0) { /* Change SO_RCVBUF? */ if(setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *) &rcvbufs, sizeof(rcvbufs)) < 0) { /* Yes, socket receive-buffer size, OK? */ if (msglev < 2) { /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s WARNING: setsockopt() SO_RCVBUF %d returned ", me, rcvbufs); perror("error"); } /* Error, but we try to go on */ } } /* End of if change SO_RCVBUF */ if (msglev < 1) /* Debuggery? */ { optlen = sizeof(unsigned int); /* Yes */ (void) getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *) &rcvbuf, &optlen); /* Socket receive-buffer size */ (void) fprintf(stderr, "%s DEBUG: rcvbufs is %u bytes \n", me, rcvbuf); } /* Get service number for data port socket service */ if ((set = getservbyname(dataport, smod)) == NULL) { /* OK? */ (void) fprintf(stderr, /* Nope */ "%s ERROR: \007 %s %s not found in /etc/services \n", me, dataport, smod); return(-6); /* Error */ } /* Allow reuse of the local socket address in bind() */ on = TRUE; if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(int)) < 0) { /* OK? */ if (msglev < 2) { /* Nope. Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s WARNING: \007 setsockopt() SO_REUSEADDR returned ", me); perror("error"); } /* Error, but we try to go on */ } /* Set configuration for bind() */ socaddin.sin_port = set->s_port; /* Port service's number */ if (msglev < 1) /* Debuggery? */ /* Yes. For Intelendian, we need to swap bytes in set->s_port */ (void) fprintf(stderr, "%s DEBUG: %s %s port is %d \n", me, dataport, smod, ntohs(socaddin.sin_port)); socaddin.sin_family = PF_INET; /* To agree with socket() above */ socaddin.sin_addr.s_addr = INADDR_ANY; /* From any network address */ /* Bind this socket to service */ if ((k = bind(sock, (struct sockaddr *) &socaddin, sizeof(struct sockaddr_in))) < 0) { /* OK? */ (void) fprintf(stderr, "%s ERROR: \007 bind() returned %d ", me, k); perror("error"); return(-7); /* Error */ } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s DEBUG: Socket %d bound to %s %s \n", me, sock, dataport, smod); /* Listen for connections on this socket */ if (type == SOCK_STREAM && (k = listen(sock, 3)) < 0) { /* OK? */ (void) fprintf(stderr, "%s ERROR: \007 listen() returned %d ", me, k); perror("error"); return(-8); /* Error */ } if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s DEBUG: Listening for connections on socket %d \n", me, sock); /* Accept a connection on this socket */ if (type == SOCK_STREAM) { /* tcp? */ kk = sizeof(struct sockaddr_in); if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s DEBUG: Waiting on accept() \n", me); /* (We usually hang here waiting for the Mark-5 machine to connect) */ if ((sock2 = accept(sock, (struct sockaddr *) &socadd, &kk)) < 0) { (void) fprintf(stderr, "%s ERROR: \007 accept() returned %d ", me, sock2); perror("error"); return(-9); /* Error */ } /* Here we have an accept on the data socket and a new socket */ if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, "%s DEBUG: Got accept() on sock %d, sock2 %d \n", me, sock, sock2); (void) close(sock); /* Accept no more connections */ } else { /* udp */ sock2 = sock; if (msglev < 1) /* Debuggery? */ (void) fprintf(stderr, /* Yes */ "%s DEBUG: Ready to read %s data on socket %d \n", me, smod, sock); } /* ** Read data from this socket and write to file ** */ while (1) { /* Loop until break */ /* Read data from socket */ len = recv(sock2, (void *) rbuff, sizeof(rbuff), 0); /* Check for errors or disconnect */ if (len < 0) { /* Errors? */ (void) fprintf(stderr, /* Yes */ "%s ERROR: recv() on sock2 %d returned %d ", me, sock2, len); perror("error"); return(-10); /* Error */ } if (len == 0) { /* Mark-5 disconnect? */ if (msglev < 0) /* Yes. Debuggery? */ (void) fprintf(stderr, " \n"); /* Yes */ if (msglev < 1) /* More debuggery? */ (void) fprintf(stderr, /* Yes */ "%s DEBUG: recv() on sock2 %d returned %d; closing \n", me, sock2, len); break; /* Out of while loop until break */ } /* Else OK, write data to file */ if ((k = fwrite((void *) rbuff, len, 1, file)) != 1) { /* OK? */ (void) fprintf(stderr, /* Nope */ "%s ERROR: fwrite() on %s returned %d ", me, filename, k); perror("error"); return(-11); /* Error */ } if (msglev < 0) /* Debuggery? */ (void) fprintf(stderr, "#"); /* Yes */ } /* End of while loop until break */ (void) fprintf(stderr, "%s: The End \n", me); return(0); /* Normal end */ } /* End of main() = Net2file */