/*
** dcc.c (for irchat-jp)
** Copyright (C) 1995,1996,1998,1999 KIKUCHI Takahiro
**
** Author: KIKUCHI Takahiro <kick@kyoto.wide.ad.jp>
** 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 <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <netdb.h>
#ifdef AIX
# include <sys/select.h>
#endif
#ifdef linux
# ifndef __GLIBC__
# include <linux/time.h>
# endif
#endif
#ifdef SOCKS
# include <socks.h>
#endif
#ifndef O_BINARY
#define O_BINARY (0)
#endif
#ifdef USE_PTHREAD
# define PTHREAD_KERNEL
# include <pthread.h>
# include <signal.h>
# include <errno.h>
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 <type> <command> <arg>...\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 <filename> <port> <ircserver>\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 <addr> <port> <size> <filename>\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 <addr> <port> <size> <filename> <filename2> \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 <port> <ircserver>\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 <addr> <port>\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 <addr> <port>\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;
}
}
syntax highlighted by Code2HTML, v. 0.9.1