/* ** dcc.c (for irchat-jp) ** Copyright (C) 1995,1996,1998,1999 KIKUCHI Takahiro ** ** Author: KIKUCHI Takahiro ** Created: Mar 19, 1995 ** Last Modified: Jul 7, 1999 */ /* ** dcc.c for irchat-pj by irchat-PJ Project ** - modified for Linux GLIBC by simm@irc.fan.gr.jp ** - modified for Microsoft Windows by quiver@tky3.3web.ne.jp ** - modified for OS/2 Warp by yuu@cb3.so-net.ne.jp ** - merge by simm@irc.fan.gr.jp, Sun, 20 Feb 2000 03:50:01 +0900 ** ** $Id: dcc.c,v 1.5 2000/11/05 15:01:53 simm Exp $ */ #include #include #include #include #include #include #include #include #include #include #ifdef AIX # include #endif #ifdef linux # ifndef __GLIBC__ # include # endif #endif #ifdef SOCKS # include #endif #ifndef O_BINARY #define O_BINARY (0) #endif #ifdef USE_PTHREAD # define PTHREAD_KERNEL # include # include # include void *wait_resume(); #endif #define DCC_NORESUME 0 #define DCC_RESUME 1 long position = 0; int main(argc, argv) int argc; char **argv; { char *command, *type, *action; int status = 1; extern u_long atoul(); #ifdef __EMX__ /* for emx OS/2 */ _fsetmode(stdout,"b"); /* stdout is binary */ _fsetmode(stderr,"b"); /* stderr is binary */ #endif command = *argv++, argc--; if (argc < 2) { printf("DCC ERROR Not enough parameters\n"); fprintf(stderr, "Usage: %s ...\n", command); exit(1); } type = *argv++, argc--; action = *argv++, argc--; if (!stricmp(type, "check")) { #ifdef USE_PTHREAD printf("DCC RESUME OK\n"); return 0; #else printf("DCC RESUME NG\n"); return 1; #endif } else if (!stricmp(type, "file")) { if (!stricmp(action, "send")) { if (argc == 1) { status = file_send(argv[0], 0, 0); } else if (argc == 2) { status = file_send(argv[0], atoi(argv[1]), 0); } else if (argc == 3) { status = file_send(argv[0], atoi(argv[1]), argv[2]); } else { printf("DCC ERROR Wrong number of parameters\n"); fprintf(stderr, "Usage: %s file send \n", command); status = 1; } } else if (!stricmp(action, "get")) { if (argc == 4 || argc == 5) { status = file_get(atoul(argv[0]), atoi(argv[1]), atoi(argv[2]), argv[3],"",DCC_NORESUME); } else { printf("DCC ERROR Wrong number of parameters\n"); fprintf(stderr, "Usage: %s file get \n", command); status = 1; } } else if (!stricmp(action, "rget")) { if (argc == 5) { status = file_get(atoul(argv[0]),atoi(argv[1]), atoi(argv[2]),argv[3],argv[4],DCC_RESUME); } else if (argc == 4) { status = file_get(atoul(argv[0]),atoi(argv[1]), atoi(argv[2]),argv[3],argv[3],DCC_RESUME); } else { printf("DCC ERROR Wrong number of parameters\n"); fprintf(stderr, "Usage: %s file rget \n", command); status = 1; } } else { printf("DCC ERROR Unsupported command %s %s\n", type, action); status = 1; } } else if (!stricmp(type, "chat")) { if (!stricmp(action, "listen")) { if (argc == 0) { status = chat_listen(0, 0); } else if (argc == 1) { status = chat_listen(atoi(argv[0]), 0); } else if (argc == 2) { status = chat_listen(atoi(argv[0]), argv[1]); } else { printf("DCC ERROR Wrong number of parameters\n"); fprintf(stderr, "Usage: %s chat listen \n", command); status = 1; } } else if (!stricmp(action, "connect")) { if (argc == 2) { status = chat_connect(atoul(argv[0]), atoi(argv[1])); } else { printf("DCC ERROR Wrong number of parameters\n"); fprintf(stderr, "Usage: %s chat connect \n", command); status = 1; } } else { printf("DCC ERROR Unsupported command %s %s\n", type, action); status = 1; } } else if (!stricmp(type, "tcp")) { if (!stricmp(action, "connect")) { if (argc == 2) { status = tcp_connect(argv[0], argv[1]); } else { printf("DCC ERROR Wrong number of parameters\n"); fprintf(stderr, "Usage: %s chat connect \n", command); status = 1; } } else { printf("DCC ERROR Unsupported command %s %s\n", type, action); status = 1; } } else { printf("DCC ERROR Unsupported command %s\n", type); status = 1; } if (status) { sleep(3); } exit(status); } #ifdef _WIN32 u_long getmyaddr(ircserver) char *ircserver; { int i; struct hostent *hp; char hostname[50]; struct in_addr addr; gethostname(hostname, sizeof(hostname)); hp = gethostbyname(hostname); for (i = 0; hp->h_addr_list[i] != 0; ++i); memcpy(&addr, hp->h_addr_list[i-1], sizeof(struct in_addr)); return ntohl(addr.s_addr); } #else u_long getmyaddr(ircserver) char *ircserver; { int i, len, dummy; u_long addr; char *p; struct hostent *hp; struct sockaddr_in server, client; addr = 0xc6290004; /* dummy addr --- rootA */ if (ircserver && (hp = gethostbyname(ircserver)) != NULL) { addr = ntohl(((struct in_addr *)hp->h_addr_list[0])->s_addr); } if ((dummy = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { printf("DCC ERROR Cannot create socket\n"); return -1; } for (i = 0, p = (char *)&server; i < sizeof(server); i++, *p++ = 0); server.sin_family = AF_INET; server.sin_addr.s_addr = htonl(addr); server.sin_port = htons(7); /* dummy port --- echo */ if (connect(dummy, (struct sockaddr *)&server, sizeof(server)) < 0) { printf("DCC ERROR Cannot connect socket\n"); return -1; } len = sizeof(client); if (getsockname(dummy, (struct sockaddr *)&client, &len) < 0) { printf("DCC ERROR Cannot getsockname\n"); return -1; } close(dummy); return ntohl(client.sin_addr.s_addr); } #endif int dcc_listen(paddr, pport, ircserver) u_long *paddr; u_short *pport; char *ircserver; { int i, s, len; char *p; struct sockaddr_in server; if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("DCC ERROR Cannot create socket\n"); return -1; } for (i = 0, p = (char *)&server; i < sizeof(server); i++, *p++ = 0); server.sin_family = AF_INET; server.sin_addr.s_addr = htonl(getmyaddr(ircserver)); server.sin_port = htons(*pport); while (bind(s, (struct sockaddr *)&server, sizeof(server)) < 0) { if (!*pport) { printf("DCC ERROR Cannot bind socket\n"); return -1; } server.sin_port = htons(++(*pport)); } if (listen(s, 1) < 0) { printf("DCC ERROR Cannot listen socket\n"); return -1; } len = sizeof(server); if (getsockname(s, (struct sockaddr *)&server, &len) < 0) { printf("DCC ERROR Cannot getsockname\n"); return -1; } *pport = ntohs(server.sin_port); *paddr = ntohl(server.sin_addr.s_addr); return s; } int dcc_connect(addr, port) u_long addr; u_short port; { int i, remote; char *p; struct sockaddr_in server; if ((remote = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("DCC ERROR Cannot create socket\n"); return -1; } for (i = 0, p = (char *)&server; i < sizeof(server); i++, *p++ = 0); server.sin_family = AF_INET; server.sin_addr.s_addr = htonl(addr); server.sin_port = htons(port); if (connect(remote, (struct sockaddr *)&server, sizeof(server)) < 0) { printf("DCC ERROR Cannot connect %u/%d\n", addr, port); return -1; } return remote; } u_long atoul(str) char *str; { u_long val = 0; while (*str) { val = val * 10 + *str - '0'; str++; } return val; } u_long dottoul(str) char *str; { u_long val, v; for (val = v = 0; *str; str++) { if (*str == '.') { val = val * 256 + v; v = 0; } else { v = v * 10 + *str - '0'; } } val = val * 256 + v; return val; } int file_send(filename, port, ircserver) char *filename; u_short port; char *ircserver; { int i, file, size, s, remote, len, count = 0; u_long addr; struct stat statbuf; u_long done = 0; u_long report; char buf[4096]; #ifdef USE_PTHREAD pthread_t thread; # ifdef ENABLE_PTHREAD_ATTR pthread_attr_t th_attr; # endif #endif if ((file = open(filename, O_RDONLY|O_BINARY)) < 0) { printf("DCC ERROR Cannot open file %s\n", filename); return 1; } if (fstat(file, &statbuf) < 0) { printf("DCC ERROR Cannot stat file %s\n", filename); return 1; } size = statbuf.st_size; if ((s = dcc_listen(&addr, &port, ircserver)) < 0) { /* error report in dcc_listen */ return 1; } /* 'Setting -> 'Waiting (send DCC SEND to remote user) */ printf("DCC SEND %u %d %d\n", addr, port, size); #ifdef USE_PTHREAD /* Create Thread wait_resume() : Waiting DCC Resume */ position = 0; # ifdef ENABLE_PTHREAD_ATTR if(pthread_attr_init(&th_attr)); pthread_attr_setdetachstate(&th_attr,PTHREAD_CREATE_DETACHED); if(pthread_create(&thread,&th_attr,wait_resume,NULL)) # else if(pthread_create(&thread,NULL,wait_resume,NULL)) # endif printf("DCC WARNING Not Support Send Resume\n"); #endif remote = accept(s, (struct sockaddr *) 0, (int *) 0); close(s); /* 'Waiting -> 'Sending (accepted from remote user) */ printf("DCC SENDING\n"); #ifdef USE_PTHREAD /* Interrupt Thread : wait_resume() */ # ifndef USE_PTHREAD_KILL pthread_cancel(thread); # else if(pthread_kill(thread,0) != ESRCH) pthread_kill(thread,SIGINT); # endif /* USE_PTHREAD_KILL */ # ifdef ENABLE_PTHREAD_ATTR pthread_attr_destroy(&th_attr); # endif /* ENABLE_PTHREAD_ATTR*/ /* Resume Point position by wait_resume() */ if(done != position) done = lseek(file,position,SEEK_SET); #endif /* USE_PTHREAD */ while ((len = read(file, buf, sizeof(buf))) > 0) { write(remote, buf, len); done += len; if (++count == 16) { count = 0; while (read(remote, &report, sizeof(u_long)) > 0 && ntohl(report) != done); printf("DCC REPORT %s %d%% (%d/%d bytes) sent\n", filename, 100 * done / size, done, size); } } while (read(remote, &report, sizeof(u_long)) > 0 && ntohl(report) != done); /* 'Sending -> end */ close(remote); close(file); return 0; } int file_get(addr, port, size, filename,filename2,resume) u_long addr; u_short port; int size; char *filename; char *filename2; u_short resume; { int i, remote, file, len, oflag, toread, count = 0; u_short rport; u_long done = 0; u_long report; char dcc[32],cmd[32],fn[256],buf[4096]; oflag = O_WRONLY|O_BINARY|O_CREAT; if(resume == DCC_NORESUME) oflag = oflag | O_TRUNC; rport = port; if ((file = open(filename,oflag, 0600)) < 0) { printf("DCC ERROR1 Cannot open file %s\n", filename); return 1; } if(resume == DCC_RESUME) { position = 0; done = lseek(file,0,SEEK_END); printf("DCC RESUME %s %d %d\n",filename2,port,done); fgets(buf,sizeof(buf),stdin); if((sscanf(buf,"%s %s %s %d %d",dcc,cmd,fn,&rport,&position) != 5) || stricmp(dcc,"DCC") || stricmp(cmd,"ACCEPT")){ printf("%s,%s,%s,%d,%d\n",dcc,cmd,fn,port,position); printf("DCC ERROR Unsupported command %s\n",buf); return 1; } if(done != position) done = lseek(file,position,SEEK_SET); } if ((remote = dcc_connect(addr, rport)) < 0) { /* error report in dcc_connect */ return 1; } /* 'Connect -> 'Getting (connected to remote user) */ printf("DCC GETTING\n"); toread = sizeof(buf); while (size - done > 0) { if (toread > size - done) { toread = size - done; } if ((len = read(remote, buf, toread)) < 0) { printf("DCC ERROR read error %s\n", filename); return 1; } write(file, buf, len); done += len; report = htonl(done); write(remote, &report, sizeof(report)); if (++count == 16) { count = 0; printf("DCC REPORT %s %d%% (%d/%d bytes) received\n", filename, 100 * done / size, done, size); } } /* 'Getting -> end */ close(remote); close(file); return 0; } int chat_listen(port, ircserver) u_short port; char *ircserver; { u_long addr; int s, remote; if ((s = dcc_listen(&addr, &port, ircserver)) < 0) { return 1; } /* 'Setting -> 'Waiting (send DCC CHAT to remote user) */ printf("DCC CHAT %u %d\n", addr, port); remote = accept(s, (struct sockaddr *) 0, (int *) 0); close(s); /* 'Waiting -> 'Active (accepted from remote user) */ printf("DCC CHATTING\n"); return loop(remote); } int chat_connect(addr, port) u_long addr; u_short port; { int remote; if ((remote = dcc_connect(addr, port)) < 0) { return 1; } /* 'Connect -> 'Active (connected to remote user) */ printf("DCC CHATTING\n"); return loop(remote); } int loop(remote) int remote; { int n, len, cnt; char *ptr, buf[1024]; fd_set rfds; while (1) { FD_ZERO(&rfds); FD_SET(0, &rfds); FD_SET(remote, &rfds); if ((n = select(FD_SETSIZE, &rfds, NULL, NULL, NULL)) == -1) { continue; } if (FD_ISSET(0, &rfds)) { if ((len = read(0, buf, sizeof(buf))) <= 0) { close(0); close(remote); if (len == 0) { return 0; } else { return 1; } } for (ptr = buf; len > 0; ptr+= cnt, len -= cnt) { if ((cnt = write(remote, ptr, len)) < 0) { close(0); close(remote); return 1; } } } if (FD_ISSET(remote, &rfds)) { if ((len = read(remote, buf, sizeof(buf))) <= 0) { close(0); close(remote); if (len == 0) { return 0; } else { return 1; } } for (ptr = buf; len > 0; ptr+= cnt, len -= cnt) { if ((cnt = write(0, ptr, len)) < 0) { close(0); close(remote); return 1; } } } } } int tcp_connect(straddr, strport) char *straddr, *strport; { int remote; #ifdef INET6 int eai; struct addrinfo hints, *ai; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ((eai = getaddrinfo(straddr, strport, &hints, &ai)) != 0) { printf("ERROR :Closing Link: [*@*] (Cannot resolve %s: %s)\n", straddr, gai_strerror(eai)); return 0; } if ((remote = socket(ai->ai_family, SOCK_STREAM, 0)) < 0) { printf("ERROR :Closing Link: [*@*] (Cannot create socket)\n"); return 0; } if (connect(remote, ai->ai_addr, ai->ai_addrlen) < 0) { printf("ERROR :Closing Link: [*@*] (Cannot connect to %s/%s)\n", straddr, strport); return 0; } #else int i; u_long addr; char *p; struct hostent *hp; struct sockaddr_in server; if (*straddr >= '0' && *straddr <= '9') { addr = dottoul(straddr); } else if ((hp = gethostbyname(straddr)) != NULL) { addr = ntohl(((struct in_addr *)hp->h_addr_list[0])->s_addr); } else { printf("ERROR :Closing Link: [*@*] (Cannot resolve %s)\n", straddr); return 0; } if ((remote = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("ERROR :Closing Link: [*@*] (Cannot create socket)\n"); return 0; } for (i = 0, p = (char *)&server; i < sizeof(server); i++, *p++ = 0); server.sin_family = AF_INET; server.sin_addr.s_addr = htonl(addr); server.sin_port = htons(atoi(strport)); if (connect(remote, (struct sockaddr *)&server, sizeof(server)) < 0) { printf("ERROR :Closing Link: [*@*] (Cannot connect to %s/%s)\n", straddr, strport); return 0; } #endif return loop(remote); } #ifdef USE_PTHREAD void *wait_resume() { char dcc[32],cmd[32],fn[256],buf[1024]; u_short port; long tposition; fgets(buf,sizeof(buf),stdin); if((sscanf(buf,"%s %s %s %d %d",dcc,cmd,fn,&port,&tposition) != 5) || stricmp(dcc,"DCC") || stricmp(cmd,"RESUME") || (strlen(fn) == 0)){ printf("DCC ERROR Unsupported command %s\n",buf); exit(1); } position = tposition; printf("DCC ACCEPT %s %d %d\n",fn,port,position); pthread_exit(0); } #endif int stricmp (const char *string1, const char *string2) { int d; for (;;) { d = tolower ((unsigned char)*string1) - tolower ((unsigned char)*string2); if (d != 0 || *string1 == 0 || *string2 == 0) return d; ++string1; ++string2; } }