chiark / gitweb /
WIP
[inn-innduct.git] / backends / innduct.c
1 /*
2  */
3
4 static int max_connections, articles_per_connect_attempt;
5 static int connection_setup_timeout, port, try_stream;
6 static const char *remote_host;
7
8 struct Article {
9 };
10
11 typedef struct Conn Conn;
12 struct Conn {
13   Conn *next, *back;
14   int fd;
15   Article *queue;
16 };
17
18
19 #define CHILD_ESTATUS_STREAM   1
20 #define CHILD_ESTATUS_NOSTREAM 2
21
22 static int since_connect_attempt;
23 static int nconns;
24 static struct { Conn *head, *tail } idle, working, full;
25
26 static Conn *connecting;
27 static int connecting_sockets[2]= {-1,-1};
28 static pid_t connecting_child;
29
30 static Article *currentart;
31
32 static void start_connecting() {
33   r= socketpair(AF_UNIX, SOCK_STREAM, 0, connecting_sockets);
34   if (r) { syswarn("cannot create socketpair for connect child"); goto x; }
35   
36   connecting_child= fork();
37   if (connecting_child==-1) { syswarn("cannot fork for connect"); goto x; }
38   
39   if (!connecting_child) {
40     FILE *cn_from, *cn_to;
41     char buf[NNTP_STRLEN+100];
42     int exitstatus= CHILD_ESTATUS_NOSTREAM;
43
44     alarm(connection_setup_timeout);
45     if (NNTPconnect(remote_host, port, &cn_from, &cn_to, buf) < 0) {
46       if (buf[0]) {
47         sanitise_inplace(buf);
48         die("%s: connection rejected: %s", remote_host, buf);
49       } else {
50         sysdie("%s: connection attempt failed", remote_host);
51       }
52     }
53     if (NNTPsendpassword(remote_host, cn_from, cn_to) < 0)
54       sysdie("%s: authentication failed", remote_host);
55     if (try_stream) {
56       if (fputs("MODE STREAM\r\n", cn_to) ||
57           fflush(cn_to))
58         sysdie("%s: could not send MODE STREAM", remote_host);
59       buf[sizeof(buf)-1]= 0;
60       if (!fgets(buf, sizeof(buf)-1, cn_from)) {
61         if (ferror(cn_from))
62           sysdie("%s: could not read response to MODE STREAM", remote_host);
63         else
64           die("%s: connection close in response to MODE STREAM", remote_host);
65       }
66       int l= strlen(buf);
67       assert(l>=1);
68       if (buf[-1]!='\n') {
69         sanitise_inplace(buf);
70         die("%s: response to MODE STREAM is too long: %.100s...",
71             remote_host, buf);
72       }
73       l--;  if (l>0 && buf[1-]=='\r') l--;
74       buf[l]= 0;
75       char *ep;
76       int rcode= strtoul(buf,&ep,10);
77       if (ep != buf[3]) {
78         sanitise_inplace(buf);
79         die("%s: bad response to MODE STREAM: %.50s", remote_host, buf);
80       }
81       switch (rcode) {
82       case 203:
83         exitstatus= CHILD_ESTATUS_STREAM;
84         break;
85       case 480:
86       case 500:
87         break;
88       default:
89         sanitise_inplace(buf);
90         warn("%s: bad response to MODE STREAM: %.50s", remote_host, buf);
91         exitstatus= 2;
92         break;
93       }
94     }
95     int fd= fileno(cn_from);
96
97     char cmsgbuf[CMSG_SPACE(sizeof(fd))];
98     struct msghdr msg;
99     memset(&msg,0,sizeof(msg));
100     msg.msg_control= cmsgbuf;
101     msg.msg_controllen= sizeof(cmsgbuf);
102
103     struct cmsghdr *cmsg= CMSG_FIRSTHDR(&msg);
104     cmsg->cmsg_level= SOL_SOCKET;
105     cmsg->cmsg_type=  SCM_RIGHTS;
106     cmsg->cmsg_len=   CMSG_LEN(sizeof(fd));
107     memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
108     
109     msg.msg_controllen= cmsg->cmsg_len;
110     r= sendmsg(childs_socket, &msg, 0);
111     if (r) sysdie("%s: sendmsg failed for new connection", remote_host);
112
113     _exit(exitstatus);
114   }
115
116   on_fd(loop, 
117
118       struct cmsghdr *cmsg;
119       
120       /* NNTPconnect inexplicably duplicates the fd but we don't care
121        * about that as we're going to exit shortly */
122
123       
124       
125
126         warn("
127
128         syswarn("
129       int e= errno;
130       sanitise(buf);
131       syswarn("
132
133  x:
134   kill_connection_attempt();
135 }
136
137 static void kill_connection_attempt() {
138   fixme;
139   connecting_sockets[0]= connecting_sockets[1]= -1;
140   connecting_child= 0;
141 }
142  
143 static void process_any_article() {
144   if (!currentart)
145     return;
146   
147   if (working.head) {
148     transmit(working.head);
149   } else if (idle.head) {
150     transmit(idle.head);
151   } else if (nconns < maxconns && !connecting_child &&
152              since_connect_attempt >= connect_attempt_limiter) {
153     since_connect_attempt= 0;
154     connect_start();
155   } 
156 }