%% options copyright owner = Dirk Krause copyright year = 2012-2014 license = bsd %% module #include "dk3all.h" #include "dk3nettp.h" #include "dkt-version.h" $!trace-include #if !DK3_HAVE_SSIZE_T #ifdef DK3_HAVE_SSIZE_T #undef DK3_HAVE_SSIZE_T #endif /** Data type for read/write operation result. */ typedef size_t ssize_t; /** Flag: ssize_t defined. */ #define DK3_HAVE_SSIZE_T 1 #endif /** @defgroup dknetoperations Program operations for dknet. */ /**@{*/ /** Program operation: Send data. */ #define DKNET_OPERATION_SEND 1 /** Program operation: Receive data. */ #define DKNET_OPERATION_RECEIVE 2 /** Program operation: Show help text. */ #define DKNET_OPERATION_HELP 4 /** Program operation: Show version number. */ #define DKNET_OPERATION_VERSION 8 /** Program operation: Show license terms. */ #define DKNET_OPERATION_LICENSE 16 /**@}*/ #if DK3_ON_WINDOWS || DK3_HAVE_SOCKET /* dknet -s -a [/] -c [] dknet -r : -t -h -v */ #if DK3_ON_WINDOWS /** Data type for socket. */ #define dknet_socket SOCKET #else /** Data type for socket. */ #define dknet_socket int #endif /** One element in the list of allowed peers. */ typedef struct { unsigned long ipaddress; /**< IPv4 address in host byte order. */ unsigned long mask; /**< Network mask. */ } dknet_allowed_peer; /** Client information. */ typedef struct { struct sockaddr_in addr; /**< IPv4 address of client. */ dknet_socket fd; /**< Socket for communication to client. */ int ncl; /**< Number of client. */ int ec; /**< Error occured. */ } dknet_client; /** Job structure. */ typedef struct { dk3_app_t *app; /**< Application structure. */ FILE *file; /**< Input/output file. */ dkChar const * const *msg; /**< Localized message texts. */ dkChar const * const *argv; /**< Command line arguments array. */ dkChar const *fn1; /**< Original file name. */ dkChar const *fn2; /**< File name after expansion. */ dk3_sto_t *s_a; /**< Storage for acceptable addresses. */ dk3_sto_it_t *i_a; /**< Iterator for acceptable addresses. */ dk3_sto_t *s_c; /**< Storage for clients. */ dk3_sto_it_t *i_c; /**< Iterator through clients storage. */ unsigned long host; /**< IP addr of server host net byte order. */ dknet_socket fd; /**< Socket. */ int exval; /**< Exit code for the program. */ int argc; /**< Number of command line arguments. */ int op; /**< Operation to perform. */ int ncl; /**< Number of clients. */ int stt; /**< Flag: Start transmission. */ unsigned short port; /**< Port number for sender, host byte order. */ } dknet_job; /** Keywords used by module, not localized. */ static dkChar const * const dknet_kw_noloc[] = { $!string-table macro=dkT # # 0: Program group name # dkt-3 # # 1: String table name # dknet4.str # # 2: Format string for sscanf # %u # # 3: Format string for sscanf # %i # # 4: Keyword "unlimited" # unlimited # # 5: File open mode for binary read # rb # # 6: File open mode for binary write # wb # # 7: Name of help text file. # dknet4.txt # # 8: Program name for version. # dknet $!end }; /** Message texts, a localized array is used if found. */ static dkChar const * const dknet_kw[] = { $!string-table file=dknet4.str,macro=dkT # # 0: File name <> used if no file name specified # <> # # 1/2: Progress message: Retrieving IP address for host "...". # Retrieving IP address for host " ". # # 3/4: Error: Host not found: "..."! # Host not found: " "! # # 5: Progress message: Socket created. # Socket created. # # 6: Progress message: Local address bound. # Local address bound. # # 7: Progress message: Listening for connection requests. # Listening for connection requests. # # 8: Progress message: Waiting for next client to connect. # Waiting for next client to connect. # # 9: Progress message: Initial data contains start command. # Initial data contains start command. # # 10: Progress message: Initial data was read. # Initial data was read. # # 11: Error: Failed to read initial data! # Failed to read initial data! # # 12: Progress message: Maximum number of clients reached. # Maximum number of clients reached. # # 13: Error: Error while accepting next client! # Failed to accept client! # # 14: Error: Failed to listen for client connection requests! # Failed to listen for client connection requests! # # 15: Progress message: Starting data transfer. # Starting data transfer. # # 16: Progress message: Data transfer finished. # Data transfer finished. # # 17: Progress message: Releasing all connections. # Releasing all connections. # # 18: Progress message: Finished. # Finished. # # 19: Error: Failed to bind local port! # Failed to bind local port! # # 20: Error: Failed to create socket! # Failed to create socket! # # 21: Progress message: Input file opened. # Input file opened. # # 22: Progress message: Transferring data from standard input. # Transferring data from standard input. # # 23: Error: No port number specified! # No port number specified! # # 24: Error: Failed to receive data from sender! # Failed to receive data from sender! # # 25: Progress message: End of data transfer. # End of data transfer. # # 26/27: Progress message: Output file "..." opened. # Output file " " opened. # # 28: Progress message: Writing output to standard output. # Writing output to standard output. # # 29: Progress message: Connection established. # Connection established. # # 30: Progress message: Initial data was sent. # Initial data was sent. # # 31: Error: Sender port not configured! # Sender port not configured! # # 32: Error: Sender host unknown! # Sender host unknown! # # 33: Error: Range overflow in port number! # Range overflow in port number! # # 34: Error: Illegal number of clients, must be non-negative! # Illegal number of clients, must be non-negative! # # 35: Error: Option -s requires an argument! # Option -s requires an argument! # # 36: Error: Option -r requires an argument! # Option -r requires an argument! # # 37: Error: Option -a requires an argument! # Option -a requires an argument! # # 38: Error: Option -c requires an argument! # Option -c requires an argument! # # 39/40: Error: Illegal option "-..."! # Illegal option "- "! # # 41: Error: Only one file name allowed! # Only one file name allowed! # # 42: Progress message: Command line processing finished. # Command line processing finished. # # 43: Error: Options -r and -s are exclusive! # Options -r and -s are exclusive! # # 44: Error: At least one option from -r, -s, -h, or -v is required! # At least one option from -r, -s, -h, or -v is required! # # 45: Progress message: Windows sockets initialized. # Windows sockets initialized. # # 46: Error: Failed to initialize Windows sockets! # Failed to initialize Windows sockets! # # 47/48: Progress message: IP address found: ... # IP address found: . # # 49/50: Progress message: Connection accepted from: ... # Connection accepted from: . # # 51/52: Warning: Connection refused from: ... # Connection refused from: . # # 53/54: Error: Failed to send data to ..., disconnected! # Failed to send data to , disconnected! # # 55/56: Progress message: Releasing connection to ... # Releasing connection to . # # 57/58: Progress message: Connecting to host ... # Connecting to host: . # # 59/60: Error: Failed to connect to: ... # Failed to connect to: ! # # 61/62: Error: Not an IP netmask: "..."! # Not an IP netmask: " "! $!end }; /** Long options. */ static dkChar const * const dknet_long_options[] = { $!string-table macro=dkT # 0 send # 1 receive # 2 accept # 3 clients # 4 start # 5 help # 6 version # 7 license-terms $!end }; /** License conditions for dknet. */ dkChar const * const dknet_license_text[] = { $!text macro=dkT License conditions ================== Copyright (c) 2012-2013, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder(s) nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. $!end }; /** English help text. */ dkChar const * const dknet_help_text[] = { $!text file=dknet.txt,macro=dkT dknet4 - Data transfer over network (IPv4 only) =============================================== Overview -------- The program transfers data over a network connection. It is restricted to IPv4, but it can be compiled and run on older systems too. If you need IPv6, please use the dknet program. Usage ----- dknet4 -s [-c ] [-a
/]* [] dknet4 -r : -t [] The -s option configures the program to run as sender. It listens for incoming connection requests on the specified port. After finishing all connections the contents of the specified file is sent over the connections. The program can use multiple connections. After establishing one connection the program listens for further connection requests until one of the following conditions is true: - The maximum number of connections specified by the -c option (default: 1) is reached or - One of the clients uses the -t option to start the transfer. The argument for the -c option is a non-negative value or the text "unlimited". The -a option can be used to restrict the clients allowed to connect. Use either IPv4 addresses or an IPv4 address / netmask pair. The default settings allow all connections. The -r option configures the program to run as receiver. The program connects to the specified sender and writes the received data to the specified file or to standard output if no filename is specified. A client can use the -t option to indicate that the last client has connected and the server can start the transmission. If the sender waits for an unlimited number of clients (-c unlimited or -c 0 was used) the last client connecting must use the -t option. The program returns exit code 0 on success, 1 on error. $!end }; #if DK3_ON_WINDOWS /** Check socket. @param s Socket to check. @return 1 on success, 0 on error. */ static int dknet_ok_socket(SOCKET s) { return((INVALID_SOCKET != s) ? 1 : 0); } /** Close socket. @param s Socket to close. @return Result from close operation. */ static int dknet_close_socket(SOCKET s) { return(closesocket(s)); } #else /** Check socket. @param s Socket to check. @return 1 on success, 0 on error. */ static int dknet_ok_socket(int s) { return((-1 < s) ? 1 : 0); } /** Close socket. @param s Socket to close. @return Result from close operation. */ static int dknet_close_socket(int s) { return(close(s)); } #endif /** Compare two registered clients. @param l Left client entry. @param r Right client entry. @param cri Comparison criteria (ignored). @return Comparison result. */ static int dknet_client_compare(void const *l, void const *r, int cri) { dknet_client const *cl; dknet_client const *cr; int back = 0; if(l) { if(r) { cl = (dknet_client const *)l; cr = (dknet_client const *)r; if(cl->ncl > cr->ncl) { back = 1; } else { if(cl->ncl < cr->ncl) { back = -1; } } } else { back = 1; } } else { if(r) { back = -1; } } return back; } /** Initialize job structure. @param job Job structure to initialize. */ static void dknet_job_init(dknet_job *job) { $? "+ dknet_job_init" job->app = NULL; job->msg = NULL; job->argv = NULL; job->fn1 = NULL; job->fn2 = NULL; job->host = 0UL; job->exval = 1; job->argc = 0; job->op = 0; job->ncl = -1; job->stt = 0; job->port = 0U; job->s_a = NULL; job->i_a = NULL; job->s_c = NULL; job->i_c = NULL; #if DK3_ON_WINDOWS job->fd = INVALID_SOCKET; #else job->fd = -1; #endif $? "- dknet_job_init" } /** Prepare job to run. @param job Job structure to prepare. @param app Application structure. @param msg Localized texts. @return 1 on success, 0 on error. */ static int dknet_job_up(dknet_job *job, dk3_app_t *app, dkChar const * const *msg) { int back = 0; $? "+ dknet_job_up" job->app = app; job->msg = msg; job->exval = 1; job->s_c = dk3sto_open_app(app); if(job->s_c) { dk3sto_set_comp(job->s_c, dknet_client_compare, 0); job->i_c = dk3sto_it_open(job->s_c); if(job->i_c) { back = 1; } } $? "- dknet_job_up %d", back return back; } /** Clean up job structure. @param job Job structure to clean up. */ static void dknet_job_cleanup(dknet_job *job) { dknet_allowed_peer *pp; dknet_client *cp; $? "+ dknet_job_cleanup" if(job->s_a) { if(job->i_a) { dk3sto_it_reset(job->i_a); while((pp = (dknet_allowed_peer *)dk3sto_it_next(job->i_a)) != NULL) { $? ". allowed %lu.%lu.%lu.%lu/%lu.%lu.%lu.%lu", ((pp->ipaddress >> 24) & 0x000000FFUL), ((pp->ipaddress >> 16) & 0x000000FFUL), ((pp->ipaddress >> 8) & 0x000000FFUL), ((pp->ipaddress) & 0x000000FFUL), ((pp->mask >> 24) & 0x000000FFUL), ((pp->mask >> 16) & 0x000000FFUL), ((pp->mask >> 8) & 0x000000FFUL), ((pp->mask) & 0x000000FFUL) pp->ipaddress = pp->mask = 0UL; dk3_delete(pp); } dk3sto_it_close(job->i_a); } dk3sto_close(job->s_a); } job->s_a = NULL; job->i_a = NULL; if(job->s_c) { if(job->i_c) { dk3sto_it_reset(job->i_c); while((cp = (dknet_client *)dk3sto_it_next(job->i_c)) != NULL) { $? ". client ncl = %d address = %lu.%lu.%lu.%lu", cp->ncl, (((cp->addr).sin_addr.s_addr >> 24) & 0x000000FFUL), (((cp->addr).sin_addr.s_addr >> 16) & 0x000000FFUL), (((cp->addr).sin_addr.s_addr >> 8) & 0x000000FFUL), (((cp->addr).sin_addr.s_addr) & 0x000000FFUL) dk3_delete(cp); } dk3sto_it_close(job->i_c); } dk3sto_close(job->s_c); } job->s_c = NULL; job->i_c = NULL; $? "- dknet_job_cleanup" } /** Write a log message containing an IP address and optionally a port number. @param app Application structure for diagnostics. @param ll Log level. @param msg Localized message texts. @param i1 First message part index in @a msg. @param i2 Last message part index in @a msg. @param ip IP address in net byte order. @param port Port number in host byte order. */ static void dknet_log_with_ip_and_port( dk3_app_t *app, int ll, dkChar const * const *msg, size_t i1, size_t i2, unsigned long ip, unsigned short port ) { dkChar bu2[256]; char bu1[256]; unsigned long iph; iph = ntohl(ip); if(port) { sprintf( bu1, "%lu.%lu.%lu.%lu:%u", ((iph >> 24) & 0x000000FFUL), ((iph >> 16) & 0x000000FFUL), ((iph >> 8) & 0x000000FFUL), ( iph & 0x000000FFUL), (unsigned)port ); } else { sprintf( bu1, "%lu.%lu.%lu.%lu", ((iph >> 24) & 0x000000FFUL), ((iph >> 16) & 0x000000FFUL), ((iph >> 8) & 0x000000FFUL), ( iph & 0x000000FFUL) ); } if(dk3str_cnv_c8_to_str_app(bu2, DK3_SIZEOF(bu2,dkChar), bu1, app)) { if(msg) { dk3app_log_3(app, ll, msg, i1, i2, bu2); } else { dk3app_log_i3(app, ll, i1, i2, bu2); } } } /** Find IPv4 address for host. @param job Job structure. @param txt Host name. @return Address in net representation on success, 0UL on error. */ static unsigned long dknet_find_host(dknet_job *job, dkChar const *txt) { unsigned long back = 0UL; $? "+ dknet_find_host \"%s\"", TR_STR(txt) /* PROGRESS: Searching for IP address for host. */ dk3app_log_3(job->app, DK3_LL_PROGRESS, job->msg, 1, 2, txt); if(dk3enc_ipaddr_to_ul_app(txt, &back, job->app)) { $? ". %lx", back back = htonl(back); } else { #if DK3_ON_WINDOWS || DK3_HAVE_GETHOSTBYNAME #if DK3_CHAR_SIZE > 1 char bu[256]; struct hostent *he; char **xptr; unsigned long *ulptr; if(dk3str_to_c8p_app(bu, sizeof(bu), txt, 0, job->app)) { he = gethostbyname(bu); if(he) { if(he->h_addrtype == AF_INET) { if(he->h_length == 4) { if(he->h_addr_list) { xptr = he->h_addr_list; ulptr = (unsigned long *)(*xptr); if(ulptr) { back = *ulptr; } } } } } } #else struct hostent *he; char **xptr; unsigned long *ulptr; he = gethostbyname(txt); if(he) { if(he->h_addrtype == AF_INET) { if(he->h_length == 4) { if(he->h_addr_list) { xptr = he->h_addr_list; ulptr = (unsigned long *)(*xptr); if(ulptr) { back = *ulptr; } } } } } #endif #endif } if(back) { /* PROGRESS: IP address found. */ dknet_log_with_ip_and_port( job->app, DK3_LL_PROGRESS, job->msg, 47, 48, back, 0 ); } else { /* ERROR: Host not found! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 3, 4, txt); } $? "- dknet_find_host %lx", back return back; } /** Set re-use flag for socket. @param s Socket to modify. */ static void dknet_set_reuse(dknet_socket s) { #if DK3_HAVE_SETSOCKOPT && defined(SOL_SOCKET) && defined(SO_REUSEADDR) int y = 1; #endif $? "+ dknet_set_reuse" #if DK3_HAVE_SETSOCKOPT && defined(SOL_SOCKET) && defined(SO_REUSEADDR) $? ". can reuse socket" (void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char const *)&y, sizeof(y)); #else $? ". setsockopt not available" #endif /* if DK3_HAVE_SETSOCKOPT && defined(SOL_SOCKET) && defined(SO_REUSEADDR) */ $? "- dknet_set_reuse" } /** Check whether a peer is allowed to connect. @param job Job structure. @param soin Address of peer. @return 1 for connection allowed, 0 otherwise. */ static int dknet_peer_ok(dknet_job *job, struct sockaddr_in *soin) { dknet_allowed_peer *pa; /* One configured allowed peer. */ int back = 0; $? "+ dknet_peer_ok" if((job->s_a) && (job->i_a)) { $? ". access restricted" dk3sto_it_reset(job->i_a); do { pa = (dknet_allowed_peer *)dk3sto_it_next(job->i_a); if(pa) { if((pa->ipaddress & pa->mask) == (pa->mask & (soin->sin_addr).s_addr)) { back = 1; $? ". match found" } } } while((pa) && (back == 0)); } else { $? ". unrestricted access" back = 1; } $? "- dknet_peer_ok %d", back return back; } /** Save one peer. @param job Job structure. @param fd Socket to communicate with peer. @param soin Peer socket address. @param nc Number of this client. @return Pointer to new peer structure on success, NULL on error. */ static dknet_client * dknet_save_peer( dknet_job *job, dknet_socket fd, struct sockaddr_in *soin, int nc ) { dknet_client *back; back = dk3_new_app(dknet_client,1,job->app); if(back) { dk3mem_cpy((void *)(&(back->addr)), (void *)soin, DK3_SZ_SOAIN); back->fd = fd; back->ncl = nc; if(!dk3sto_add(job->s_c, (void *)back)) { dk3_delete(back); back = NULL; } } return back; } /** Do orderly release for a socket. @param job Job structure. @param fd Socket file descriptor. */ static void dknet_orderly_release(dknet_job *job, dknet_socket fd) { char bu[512]; int cc; ssize_t rb; #if DK3_ON_WINDOWS shutdown(fd, SD_SEND); #else #if defined(SHUT_WR) shutdown(fd, SHUT_WR); #else shutdown(fd, 1); #endif #endif do { cc = 0; rb = recv(fd, (void *)bu, sizeof(bu), 0); if(rb > 0) { cc = 1; } } while(cc); } /** Run sender when data source is open. @param job Job structure. */ static void dknet_run_send_file(dknet_job *job) { char bu[4096]; /* Data buffer. */ struct sockaddr_in soin; /* Sender local address. */ struct sockaddr_in clsoin; /* Client address. */ dknet_client *cp; /* New client data. */ size_t rb; /* Number of bytes read from file. */ ssize_t wb; /* Number of bytes written to socket. */ #if DK3_ON_WINDOWS int sz; /* Size of address. */ #else socklen_t sz; /* Size of address. */ #endif dknet_socket clfd; /* Client socket fd. */ int cc; /* Flag: Can continue. */ int ok = 0; /* Flag: No errors occured. */ int nc = 0; /* Next client. */ char ch; /* Byte to receive. */ $? "+ dknet_run_send_file" job->fd = socket(PF_INET, SOCK_STREAM, 0); if(dknet_ok_socket(job->fd)) { /* PROGRESS: Socket created. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 5); soin.sin_family = AF_INET; soin.sin_port = htons(job->port); soin.sin_addr.s_addr = htonl(INADDR_ANY); dknet_set_reuse(job->fd); if(bind(job->fd, (struct sockaddr *)(&soin), DK3_SZ_SOAIN) == 0) { /* PROGRESS: Local address bound. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 6); /* Listen for connections and save peer data. */ if(listen(job->fd, 2) == 0) { /* PROGRESS: Listening for connection requests. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 7); ok = 1; do { cc = 0; sz = DK3_SZ_SOAIN; /* PROGRESS: Waiting for the next client to connect. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 8); clfd = accept(job->fd, (struct sockaddr *)(&clsoin), &sz); if(dknet_ok_socket(clfd)) { cc = 1; if(dknet_peer_ok(job, &clsoin)) { if((cp = dknet_save_peer(job, clfd, &clsoin, nc)) != NULL) { /* PROGRESS: Connection from peer accepted. */ dknet_log_with_ip_and_port( job->app, DK3_LL_PROGRESS, job->msg, 49, 50, clsoin.sin_addr.s_addr, ntohs(clsoin.sin_port) ); nc++; if(recv(clfd, (void *)(&ch), sizeof(ch), 0) == 1) { if(ch != 0x00) { cc = 0; /* PROGRESS: Initial data contains start command. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 9); } else { /* PROGRESS: Initial data was read. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 10); } } else { /* ERROR: Failed to read initial data! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 11); } if(cc) { if(job->ncl > 0) { if(nc >= job->ncl) { cc = 0; /* Progress: Number of clients reached. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 12); } } } } else { ok = 0; /* ERROR: Failed to save peer! */ dknet_orderly_release(job, job->fd); dknet_close_socket(clfd); } } else { /* INFO: Connection refused! */ dknet_log_with_ip_and_port( job->app, DK3_LL_WARNING, job->msg, 51, 52, clsoin.sin_addr.s_addr, ntohs(clsoin.sin_port) ); dknet_close_socket(clfd); } } else { ok = 0; /* ERROR: Error from accept! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 13); } } while(cc); } else { /* ERROR: Listen failed! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 14); } /* Write data. */ if(ok) { /* PROGRESS: Starting data transfer. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 15); job->exval = 0; if(nc == 1) { $? ". just one client" dk3sto_it_reset(job->i_c); cp = (dknet_client *)dk3sto_it_next(job->i_c); if(cp) { do { cc = 0; rb = dk3sf_fread_app(bu, 1, sizeof(bu), job->file, job->app); if(rb > 0) { cc = 1; if(!(cp->ec)) { #if DK3_ON_WINDOWS wb = send(cp->fd, (const void *)bu, (int)rb, 0); #else wb = send(cp->fd, (const void *)bu, rb, 0); #endif if(wb < (ssize_t)rb) { cp->ec = 1; /* ERROR: Failed to send data to peer, disconnect! */ job->exval = 1; dknet_log_with_ip_and_port( job->app, DK3_LL_ERROR, job->msg, 53, 54, (cp->addr).sin_addr.s_addr, ntohs((cp->addr).sin_port) ); } } } } while(cc); } } else { $? ". multiple clients" do { cc = 0; rb = dk3sf_fread_app(bu, 1, sizeof(bu), job->file, job->app); if(rb > 0) { cc = 1; dk3sto_it_reset(job->i_c); while((cp = (dknet_client *)dk3sto_it_next(job->i_c)) != NULL) { if(!(cp->ec)) { #if DK3_ON_WINDOWS wb = send(cp->fd, (const void *)bu, (int)rb, 0); #else wb = send(cp->fd, (const void *)bu, rb, 0); #endif if(wb < (ssize_t)rb) { cp->ec = 1; /* ERROR: Failed to send data to peer, disconnect! */ job->exval = 1; dknet_log_with_ip_and_port( job->app, DK3_LL_ERROR, job->msg, 53, 54, (cp->addr).sin_addr.s_addr, ntohs((cp->addr).sin_port) ); } } } } } while(cc); } /* PROGRESS: Data transfer finished. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 16); } /* Close all connections. */ /* PROGRESS: Releasing all connections. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 17); dk3sto_it_reset(job->i_c); while((cp = (dknet_client *)dk3sto_it_next(job->i_c)) != NULL) { /* PROGRESS: Releasing connection to peer. */ dknet_log_with_ip_and_port( job->app, DK3_LL_PROGRESS, job->msg, 55, 56, (cp->addr).sin_addr.s_addr, ntohs((cp->addr).sin_port) ); if(!(cp->ec)) { dknet_orderly_release(job, cp->fd); } dknet_close_socket(cp->fd); } /* PROGRESS: Finished. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 18); } else { /* ERROR: Failed to bind local address! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 19); } dknet_close_socket(job->fd) ; job->fd = -1; } else { /* ERROR: Failed to created listener socket! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 20); } $? "- dknet_run_send_file" } /** Run as sender. @param job Job structure. */ static void dknet_run_send(dknet_job *job) { dkChar fn1[DK3_MAX_PATH]; /* File name copy. */ dkChar fn2[DK3_MAX_PATH]; /* Real file name. */ dkChar const *en; /* Entry name. */ dk3_dir_t *dir; /* File name expander. */ #if DK3_ON_WINDOWS int oldmode; /* File mode for standard input. */ #endif $? "+ dknet_run_send" /* If no client number is configured, expect one client. */ if(job->ncl < 0) { job->ncl = 1; } if(job->port > 0) { if(job->fn1) { if(dk3str_len(job->fn1) < DK3_SIZEOF(fn1,dkChar)) { dk3str_cpy_not_overlapped(fn1, job->fn1); dk3str_correct_filename(fn1); if(dk3sf_must_expand(fn1)) { dir = dk3dir_fne_open_app(fn1, job->app); if(dir) { if(1 == dk3dir_get_number_of_files(dir)) { en = dk3dir_get_fullname(dir); if(en) { if(dk3str_len(en) < DK3_SIZEOF(fn2,dkChar)) { dk3str_cpy_not_overlapped(fn2, en); job->fn2 = fn2; job->file = dk3sf_fopen_app(fn2, dknet_kw_noloc[5], job->app); if(job->file) { /* PROGRESS: Input file opened. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 21); dknet_run_send_file(job); fclose(job->file); job->file = NULL; } job->fn2 = NULL; } else { /* ERROR: File name too long! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 65, 66, en); } } else { /* ERROR: No file name matches pattern! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 215, 216, fn1); } } else { if(dk3dir_get_number_of_files(dir)) { /* ERROR: Too many file names match pattern! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 168, 169, fn1); } else { /* ERROR: No file name matches pattern! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 215, 216, fn1); } } dk3dir_close(dir); } } else { job->file = dk3sf_fopen_app(fn1, dknet_kw_noloc[5], job->app); if(job->file) { /* PROGRESS: Input file opened. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 21); job->fn2 = fn1; dknet_run_send_file(job); fclose(job->file); job->file = NULL; job->fn2 = NULL; } } } else { /* ERROR: File name too long! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 65, 66, job->fn1); } } else { #if DK3_ON_WINDOWS oldmode = _setmode(_fileno(stdin), _O_BINARY); #endif job->file = stdin; job->fn2 = dknet_kw[0]; /* PROGRESS: Transferring data from standard input. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 22); dknet_run_send_file(job); job->file = NULL; #if DK3_ON_WINDOWS _setmode(_fileno(stdin), oldmode); #endif } } else { /* ERROR: No port number specified! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 23); } $? "- dknet_run_send" } /** Run as receiver with file and socket. @param job Job structure. */ static void dknet_run_receive_with_file_and_socket(dknet_job *job) { char bu[4096]; ssize_t rb; int cc; do { cc = 0; rb = recv(job->fd, (void *)bu, sizeof(bu), 0); if(rb > 0) { cc = 1; if(job->file) { if(job->exval == 0) { if(!dk3sf_fwrite_app( (void const *)bu, 1, (size_t)rb, job->file, job->app ) ) { /* ERROR: File write! */ dk3app_log_i1(job->app, DK3_LL_ERROR, 120); job->exval = 1; } } } } else { if(rb < 0) { /* ERROR: Error during net read! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 24); job->exval = 1; } else { /* PROGRESS: End of transfer. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 25); } } } while(cc); } /** Run receiver with socket. Open file and continue. @param job Job structure. */ static void dknet_run_receive_with_socket(dknet_job *job) { #if DK3_ON_WINDOWS int oldmode; /* File mode for standard input. */ #endif if(job->fn2) { job->file = dk3sf_fopen_app(job->fn2, dknet_kw_noloc[6], job->app); if(job->file) { /* PROGRESS: Output file opened. */ dk3app_log_3(job->app, DK3_LL_PROGRESS, job->msg, 26, 27, job->fn2); job->exval = 0; } dknet_run_receive_with_file_and_socket(job); if(job->file) { if(!dk3sf_fclose_fn_app(job->file, job->fn2, job->app)) { job->exval = 1; } job->file = NULL; } } else { #if DK3_ON_WINDOWS oldmode = _setmode(_fileno(stdout), _O_BINARY); #endif job->file = stdout; job->exval = 0; /* PROGRESS: Writing output to standard output. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 28); dknet_run_receive_with_file_and_socket(job); job->file = NULL; #if DK3_ON_WINDOWS _setmode(_fileno(stdout), oldmode); #endif } } /** Run receiver with a given file name. @param job Job structure. */ static void dknet_run_receive_filename(dknet_job *job) { struct sockaddr_in soin; char ch = 0x00; $? "+ dknet_run_receive_filename \"%s\"", TR_STR(job->fn2) job->fd = socket(PF_INET, SOCK_STREAM, 0); if(dknet_ok_socket(job->fd)) { /* PROGRESS: Socket created. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 5); soin.sin_family = AF_INET; soin.sin_port = htons(job->port); soin.sin_addr.s_addr = job->host; dknet_set_reuse(job->fd); /* PROGRESS: Connecting to host. */ dknet_log_with_ip_and_port( job->app, DK3_LL_PROGRESS, job->msg, 57, 58, job->host, job->port ); if(0 == connect(job->fd, (struct sockaddr *)(&soin), DK3_SZ_SOAIN)) { /* PROGRESS: Connection established. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 29); if(job->stt) { ch = 0x01; } (void)send(job->fd, (void const *)(&ch), 1, 0); #if DK3_ON_WINDOWS shutdown(job->fd, SD_SEND); #else #if defined(SHUT_WR) shutdown(job->fd, SHUT_WR); #else shutdown(job->fd, 1); #endif #endif /* PROGRESS: Initial data was sent. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 30); dknet_run_receive_with_socket(job); } else { /* ERROR: Failed to connect! */ dknet_log_with_ip_and_port( job->app, DK3_LL_ERROR, job->msg, 59, 60, job->host, job->port ); } dknet_close_socket(job->fd); #if DK3_ON_WINDOWS job->fd = INVALID_SOCKET; #else job->fd = -1; #endif } else { /* ERROR: Failed to create socket! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 20); } $? "- dknet_run_receive_filename" } /** Run as receiver. @param job Job structure. */ static void dknet_run_receive(dknet_job *job) { dkChar fn1[DK3_MAX_PATH]; /* Copy of file name. */ dkChar const *en; /* Entry name. */ dk3_dir_t *dir; /* File name expander. */ $? "+ dknet_run_receive" if(job->host) { $? ". host ok" if(job->port) { $? ". port ok" if(job->fn1) { $? ". fn1" if(dk3str_len(job->fn1) < DK3_SIZEOF(fn1,dkChar)) { $? ". length" dk3str_cpy_not_overlapped(fn1, job->fn1); dk3str_correct_filename(fn1); if(dk3sf_must_expand(fn1)) { $? ". expand" dir = dk3dir_fne_open_app(fn1, job->app); if(dir) { $? ". dir" if(1 == dk3dir_get_number_of_files(dir)) { $? ". 1" en = dk3dir_get_fullname(dir); if(en) { $? ". en ok" job->fn2 = en; dknet_run_receive_filename(job); job->fn2 = NULL; } else { $? "! en" /* ERROR: No file name matches pattern! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 215, 216, fn1); } } else { $? "! number" if(dk3dir_get_number_of_files(dir)) { /* ERROR: Too many file names match pattern! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 168, 169, fn1); } else { /* ERROR: No file name matches pattern! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 215, 216, fn1); } } dk3dir_close(dir); } else { $? "! dir" } } else { $? ". no exp" job->fn2 = fn1; dknet_run_receive_filename(job); job->fn2 = NULL; } } else { $? "! length" /* ERROR: File name too long! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 65, 66, job->fn1); } } else { $? ". no fn1" dknet_run_receive_filename(job); } } else { /* ERROR: Sender port unknown! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 31); } } else { $? "! host" /* ERROR: Sender host unknown! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 32); } $? "- dknet_run_receive" } /** Show version number. @param job Job structure. */ static void dknet_show_version(dknet_job *job) { $? "+ dknet_show_version" dk3sf_initialize_stdout(); dk3sf_fputs(dknet_kw_noloc[8], stdout); dk3sf_fputc(dkT(' '), stdout); dk3sf_fputs(DKT_VERSION, stdout); dk3sf_fputc(dkT('\n'), stdout); $? "- dknet_show_version" } /** Show license conditions. @param job Job structure. */ static void dknet_show_license(dknet_job *job) { dkChar const * const *sptr; dk3sf_initialize_stdout(); sptr = dknet_license_text; while(*sptr) { dk3sf_fputs(*(sptr++), stdout); dk3sf_fputc(dkT('\n'), stdout); } } /** Show help text. @param job Job structure. */ static void dknet_show_help(dknet_job *job) { $? "+ dknet_show_help" dk3sf_initialize_stdout(); dk3app_help(job->app, dknet_kw_noloc[7], dknet_help_text); $? "- dknet_show_help" } /** Register data to run as sender. @param job Job structure. @param txt Option argument. @return 1 on success, 0 on error. */ static int dknet_register_send(dknet_job *job, dkChar const *txt) { dkChar bu[256]; /* Temporary buffer. */ unsigned short us; /* Port number. */ unsigned u; /* Conversion result. */ int back = 0; $? "+ dknet_register_send" if(dk3str_len(txt) < DK3_SIZEOF(bu,dkChar)) { dk3str_cpy_not_overlapped(bu, txt); #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(txt, dknet_kw_noloc[2], &u) == 1) #else if(0 != dk3ma_ui_from_string(&u, txt, NULL)) #endif { us = (unsigned short)u; #if VERSION_BEFORE_20140809 if((unsigned)us == u) #else if (u <= (unsigned)(DK3_US_MAX)) #endif { job->port = us; back = 1; } else { /* ERROR: Range overflow! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 33); } } else { /* ERROR: Not a number! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 141, 142, txt); } } else { /* ERROR: Option argument too long! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 135, 136, txt); } if(back) { job->op = DKNET_OPERATION_SEND; } $? "- dknet_register_send %d", back return back; } /** Register data to run as receiver. @param job Job structure. @param txt Option argument. @return 1 on success, 0 on error. */ static int dknet_register_receive(dknet_job *job, dkChar const *txt) { dkChar bu[256]; /* Temporary buffer. */ dkChar *p1; /* Port text. */ int back = 0; unsigned u; /* Conversion result. */ unsigned short us; /* Port number. */ $? "+ dknet_register_receive" if(dk3str_len(txt) < DK3_SIZEOF(bu,dkChar)) { dk3str_cpy_not_overlapped(bu, txt); p1 = dk3str_chr(bu, dkT(':')); if(p1) { *(p1++) = dkT('\0'); p1 = dk3str_start(p1, NULL); if(p1) { job->host = dknet_find_host(job, bu); if(job->host) { #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(p1, dknet_kw_noloc[2], &u) == 1) #else if(0 != dk3ma_ui_from_string(&u, p1, NULL)) #endif { us = (unsigned short)u; #if VERSION_BEFORE_20140809 if((unsigned)us == u) #else if (u <= (unsigned)(DK3_US_MAX)) #endif { job->port = us; back = 1; } else { /* ERROR: Numeric overflow */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 33); } } else { /* ERROR: Not a number! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 141, 142, p1); } } else { /* ERROR: Host not found, already reported */ } } else { /* ERROR: No port specified! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 31); } } else { /* ERROR: No port specified! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 31); } } else { /* ERROR: Option argument too long! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 135, 136, txt); } if(back) { job->op |= DKNET_OPERATION_RECEIVE; } $? "- dknet_register_receive %d", back return back; } /** Register client address. @param job Job structure. @param txt Option argument. @return 1 on success, 0 on error. */ static int dknet_register_accept(dknet_job *job, dkChar const *txt) { dkChar bu[256]; /* Temporary buffer. */ dkChar *p1; /* Separator */ dknet_allowed_peer *pp; unsigned long addr; /* IP address. */ unsigned long mask; /* Network mask. */ int back = 0; $? "+ dknet_register_accept" if(dk3str_len(txt) < DK3_SIZEOF(bu,dkChar)) { dk3str_cpy_not_overlapped(bu, txt); p1 = dk3str_chr(bu, dkT('/')); if(p1) { *(p1++) = dkT('\0'); p1 = dk3str_start(p1, NULL); } mask = 0xFFFFFFFFUL; addr = dknet_find_host(job, bu); if(addr) { $? ". addr = %lx", addr back = 1; if(p1) { if(dk3enc_ipaddr_to_ul_app(p1, &mask, job->app)) { mask = htonl(mask); } else { back = 0; /* ERROR: Not an IP netmask! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 61, 62, p1); } $? ". mask = %lx", mask } if(back) { back = 0; if(!(job->s_a)) { job->s_a = dk3sto_open_app(job->app); } if(!(job->i_a)) { if(job->s_a) { job->i_a = dk3sto_it_open(job->s_a); } } if((job->s_a) && (job->i_a)) { pp = dk3_new(dknet_allowed_peer,1); if(pp) { pp->ipaddress = addr; pp->mask = mask; if(dk3sto_add(job->s_a, (void *)pp)) { back = 1; } else { dk3_delete(pp); } } } if(!(back)) { /* ERROR: Memory */ dk3app_log_i1(job->app, DK3_LL_ERROR, 9); } } } else { /* ERROR: Host not found, already reported. */ } } else { /* ERROR: Option argument too long! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 135, 136, txt); } $? "- dknet_register_accept %d", back return back; } /** Register number of clients. @param job Job structure. @param txt Option argument. @return 1 on success, 0 on error. */ static int dknet_register_clients(dknet_job *job, dkChar const *txt) { dkChar bu[256]; /* Temporary buffer. */ int back = 0; int i; /* Conversion result. */ $? "+ dknet_register_clients \"%s\"", TR_STR(txt) if(dk3str_len(txt) < DK3_SIZEOF(bu,dkChar)) { $? ". length ok" dk3str_cpy_not_overlapped(bu, txt); if(dk3str_cmp(bu, dknet_kw_noloc[4]) == 0) { $? ". unlimited" job->ncl = 0; back = 1; } else { $? ". number" #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(bu, dknet_kw_noloc[3], &i) == 1) #else if(0 != dk3ma_i_from_string(&i, bu, NULL)) #endif { $? ". sscanf" if(i >= 0) { $? ". number ok" job->ncl = i; back = 1; } else { $? "! < 0" /* ERROR: Illegal number of clients! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 34); } } else { $? "! sscanf" /* ERROR: Not a valid number of clients */ dk3app_log_i3(job->app, DK3_LL_ERROR, 141, 142, bu); } } } else { $? "! too long" /* ERROR: Option argument too long! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 135, 136, txt); } $? "- dknet_register_clients %d", back return back; } /** Process one long option. @param job Job structure. @param txt Long option text. @return 1 on success, 0 on error. */ static int dknet_long_option(dknet_job *job, dkChar const *txt) { dkChar bu[256]; /* Buffer for option. */ dkChar *v; /* Option value. */ int back = 0; int ind; /* Index in dknet_long_opt array. */ $? "+ dknet_long_option" if(dk3str_len(txt) < DK3_SIZEOF(bu,dkChar)) { dk3str_cpy_not_overlapped(bu, txt); v = dk3str_chr(bu, dkT('=')); if(v) { *(v++) = dkT('\0'); } ind = dk3str_array_index(dknet_long_options, bu, 0); switch(ind) { case 0: { if(v) { back = dknet_register_send(job, v); } else { /* ERROR: Option requires an argument! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 133, 134, bu); } } break; case 1: { if(v) { back = dknet_register_receive(job, v); } else { /* ERROR: Option requires an argument! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 133, 134, bu); } } break; case 2: { if(v) { back = dknet_register_accept(job, v); } else { /* ERROR: Option requires an argument! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 133, 134, bu); } } break; case 3: { if(v) { back = dknet_register_clients(job, v); } else { /* ERROR: Option requires an argument! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 133, 134, bu); } } break; case 4: { job->stt = 1; back = 1; } break; case 5: { job->op |= DKNET_OPERATION_HELP; back = 1; } break; case 6: { job->op |= DKNET_OPERATION_VERSION; back = 1; } break; case 7: { job->op |= DKNET_OPERATION_LICENSE; back = 1; } break; } } else { /* ERROR: Option argument too long! */ dk3app_log_i3(job->app, DK3_LL_ERROR, 135, 136, txt); } $? "- dknet_long_option %d", back return back; } /** Run with network initialized. @param job Job structure. */ static void dknet_run_with_network(dknet_job *job) { dkChar const * const *lfdptr; /* Traverse command line arguments. */ dkChar const *ptr; /* Current command line arg. */ int i; /* Current command line arg index. */ int ok; /* Flag: No error yet. */ $? "+ dknet_run_with_network" ok = 1; i = 1; lfdptr = job->argv; lfdptr++; while(i < job->argc) { ptr = *lfdptr; $? ". current arg=\"%s\"", TR_STR(ptr) if(dkT('-') == *ptr) { $? ". option" ptr++; switch(*ptr) { case dkT('-'): { $? ". long option" ptr++; if(!dknet_long_option(job, ptr)) { ok = 0; } } break; case dkT('s'): { $? ". send" ptr++; if(!(*ptr)) { ptr = NULL; i++; lfdptr++; if(i < job->argc) { ptr = *lfdptr; } } if(ptr) { if(!dknet_register_send(job, ptr)) { ok = 0; } } else { /* ERROR: Option -s needs an argument! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 35); } } break; case dkT('r'): { $? ". receive" ptr++; if(!(*ptr)) { ptr = NULL; i++; lfdptr++; if(i < job->argc) { ptr = *lfdptr; } } if(ptr) { if(!dknet_register_receive(job, ptr)) { ok = 0; } } else { /* ERROR: Option -r needs an argument! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 36); } } break; case dkT('h'): { $? ". show help" job->op |= DKNET_OPERATION_HELP; } break; case dkT('v'): { $? ". show version" job->op |= DKNET_OPERATION_VERSION; } break; case dkT('L'): { job->op |= DKNET_OPERATION_LICENSE; } break; case dkT('a'): { $? ". accept client" ptr++; if(!(*ptr)) { ptr = NULL; i++; lfdptr++; if(i < job->argc) { ptr = *lfdptr; } } if(ptr) { if(!dknet_register_accept(job, ptr)) { ok = 0; } } else { /* ERROR: Option -a needs an argument! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 37); } } break; case dkT('c'): { $? ". number of clients" ptr++; if(!(*ptr)) { ptr = NULL; i++; lfdptr++; if(i < job->argc) { ptr = *lfdptr; } } if(ptr) { if(!dknet_register_clients(job, ptr)) { ok = 0; } } else { /* ERROR: Option -c needs an argument! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 38); } } break; case dkT('t'): { $? ". start transmission" job->stt = 1; } break; default: { $? "! illegal option" ok = 0; job->op |= DKNET_OPERATION_HELP; /* ERROR: Illegal option! */ dk3app_log_3(job->app, DK3_LL_ERROR, job->msg, 39, 40, ptr); } break; } } else { $? ". filename" if(job->fn1) { ok = 0; /* ERROR: Only one file name allowed! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 41); } else { job->fn1 = ptr; } } lfdptr++; i++; } if(ok) { /* PROGRESS: Command line processing finished. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 42); if((job->op) & DKNET_OPERATION_SEND) { if((job->op) & DKNET_OPERATION_RECEIVE) { $? "! both" /* ERROR: -s and -r are exclusive! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 43); dknet_show_version(job); dknet_show_help(job); } else { $? ". send" dknet_run_send(job); } } else { if((job->op) & DKNET_OPERATION_RECEIVE) { $? ". receive" dknet_run_receive(job); } else { $? "! none" if(job->op) { $? ". help or version" dknet_show_version(job); if((job->op) & DKNET_OPERATION_LICENSE) { dknet_show_license(job); } if((job->op) & DKNET_OPERATION_HELP) { dknet_show_help(job); } job->exval = 0; } else { $? "! none" /* ERROR: One option required! */ dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 44); dknet_show_version(job); dknet_show_help(job); } } } } else { if((job->op) & DKNET_OPERATION_HELP) { dknet_show_version(job); dknet_show_help(job); } } $? "- dknet_run_with_network" } /** Run the program, initialize network on Windows systems. @param job Job structure. @param argc number of command line arguments. @param argv Command line arguments array. */ static void dknet_run_for_job(dknet_job *job, int argc, dkChar const * const *argv) { #if DK3_ON_WINDOWS WORD vrq; WSADATA wsa; job->argc = argc; job->argv = argv; vrq = MAKEWORD(2,0); if(WSAStartup(vrq, &wsa) == 0) { /* PROGRESS: Windows sockets initialized. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 45); dknet_run_with_network(job); WSACleanup(); } else { /* ERROR: Failed to initialize network. */ dk3app_log_1(job->app, DK3_LL_PROGRESS, job->msg, 46); } #else job->argc = argc; job->argv = argv; dknet_run_with_network(job); #endif } /** Entry point of the program. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, any other value indicates an error. */ DK3_MAIN { dknet_job job; /* Job structure. */ dk3_app_t *app; /* Application structure. */ dkChar const * const *msg; /* Localized message texts. */ int exval; /* Exit code. */ $!trace-init dknet.deb $? "+ main" dknet_job_init(&job); job.exval = exval = 1; app = dk3app_open_command( argc, (dkChar const * const *)argv, dknet_kw_noloc[0] ); if(app) { msg = dk3app_messages(app, dknet_kw_noloc[1], (dkChar const **)dknet_kw); if(msg) { if(dknet_job_up(&job, app, msg)) { dknet_run_for_job( &job, dk3app_get_argc(app), dk3app_get_argv(app) ); } } else { $? "! Bug, should not happen" } dk3app_close(app); } else { /* ERROR: Memory */ fputs("ERROR: Not enough memory (RAM/swap space)\n", stderr); fflush(stderr); } exval = job.exval; dknet_job_cleanup(&job); $? "- main %d", exval $!trace-end fflush(stdout); exit(exval); return exval; } #else #error "No socket functionality available!" #endif