]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - portfwd.c
Implement `portfwd-loopback-choice'. Works on local side in Unix as
[PuTTY.git] / portfwd.c
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #include "putty.h"
5 #include "ssh.h"
6
7 #ifndef FALSE
8 #define FALSE 0
9 #endif
10 #ifndef TRUE
11 #define TRUE 1
12 #endif
13
14 #define GET_32BIT_LSB_FIRST(cp) \
15   (((unsigned long)(unsigned char)(cp)[0]) | \
16   ((unsigned long)(unsigned char)(cp)[1] << 8) | \
17   ((unsigned long)(unsigned char)(cp)[2] << 16) | \
18   ((unsigned long)(unsigned char)(cp)[3] << 24))
19
20 #define PUT_32BIT_LSB_FIRST(cp, value) ( \
21   (cp)[0] = (value), \
22   (cp)[1] = (value) >> 8, \
23   (cp)[2] = (value) >> 16, \
24   (cp)[3] = (value) >> 24 )
25
26 #define GET_16BIT_LSB_FIRST(cp) \
27   (((unsigned long)(unsigned char)(cp)[0]) | \
28   ((unsigned long)(unsigned char)(cp)[1] << 8))
29
30 #define PUT_16BIT_LSB_FIRST(cp, value) ( \
31   (cp)[0] = (value), \
32   (cp)[1] = (value) >> 8 )
33
34 #define GET_32BIT_MSB_FIRST(cp) \
35   (((unsigned long)(unsigned char)(cp)[0] << 24) | \
36   ((unsigned long)(unsigned char)(cp)[1] << 16) | \
37   ((unsigned long)(unsigned char)(cp)[2] << 8) | \
38   ((unsigned long)(unsigned char)(cp)[3]))
39
40 #define PUT_32BIT_MSB_FIRST(cp, value) ( \
41   (cp)[0] = (value) >> 24, \
42   (cp)[1] = (value) >> 16, \
43   (cp)[2] = (value) >> 8, \
44   (cp)[3] = (value) )
45
46 #define GET_16BIT_MSB_FIRST(cp) \
47   (((unsigned long)(unsigned char)(cp)[0] << 8) | \
48   ((unsigned long)(unsigned char)(cp)[1]))
49
50 #define PUT_16BIT_MSB_FIRST(cp, value) ( \
51   (cp)[0] = (value) >> 8, \
52   (cp)[1] = (value) )
53
54 struct pfwd_queue {
55     struct pfwd_queue *next;
56     char *buf;
57 };
58
59 struct PFwdPrivate {
60     struct plug_function_table *fn;
61     /* the above variable absolutely *must* be the first in this structure */
62     void *c;                           /* (channel) data used by ssh.c */
63     void *backhandle;                  /* instance of SSH backend itself */
64     /* Note that backhandle need not be filled in if c is non-NULL */
65     Socket s;
66     char hostname[128];
67     int throttled, throttle_override;
68     int port;
69     int ready;
70     struct pfwd_queue *waiting;
71 };
72
73 void pfd_close(Socket s);
74
75
76 static int pfd_closing(Plug plug, char *error_msg, int error_code,
77                        int calling_back)
78 {
79     struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;
80
81     /*
82      * We have no way to communicate down the forwarded connection,
83      * so if an error occurred on the socket, we just ignore it
84      * and treat it like a proper close.
85      */
86     sshfwd_close(pr->c);
87     pfd_close(pr->s);
88     return 1;
89 }
90
91 static int pfd_receive(Plug plug, int urgent, char *data, int len)
92 {
93     struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;
94     if (pr->ready) {
95         if (sshfwd_write(pr->c, data, len) > 0) {
96             pr->throttled = 1;
97             sk_set_frozen(pr->s, 1);
98         }
99     }
100     return 1;
101 }
102
103 static void pfd_sent(Plug plug, int bufsize)
104 {
105     struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;
106
107     sshfwd_unthrottle(pr->c, bufsize);
108 }
109
110 /*
111  * Called when receiving a PORT OPEN from the server
112  */
113 char *pfd_newconnect(Socket *s, char *hostname, int port, void *c)
114 {
115     static struct plug_function_table fn_table = {
116         pfd_closing,
117         pfd_receive,
118         pfd_sent,
119         NULL
120     };
121
122     SockAddr addr;
123     char *err, *dummy_realhost;
124     struct PFwdPrivate *pr;
125
126     /*
127      * Try to find host.
128      */
129     addr = sk_namelookup(hostname, &dummy_realhost);
130     if ((err = sk_addr_error(addr)))
131         return err;
132
133     /*
134      * Open socket.
135      */
136     pr = (struct PFwdPrivate *) smalloc(sizeof(struct PFwdPrivate));
137     pr->fn = &fn_table;
138     pr->throttled = pr->throttle_override = 0;
139     pr->ready = 1;
140     pr->c = c;
141     pr->backhandle = NULL;             /* we shouldn't need this */
142
143     pr->s = *s = new_connection(addr, dummy_realhost, port, 0, 1, 0, (Plug) pr);
144     if ((err = sk_socket_error(*s))) {
145         sfree(pr);
146         return err;
147     }
148
149     sk_set_private_ptr(*s, pr);
150     sk_addr_free(addr);
151     return NULL;
152 }
153
154 /*
155  called when someone connects to the local port
156  */
157
158 static int pfd_accepting(Plug p, void *sock)
159 {
160     static struct plug_function_table fn_table = {
161         pfd_closing,
162         pfd_receive,
163         pfd_sent,
164         NULL
165     };
166     struct PFwdPrivate *pr, *org;
167     Socket s;
168     char *err;
169
170     org = (struct PFwdPrivate *)p;
171     pr = (struct PFwdPrivate *) smalloc(sizeof(struct PFwdPrivate));
172     pr->fn = &fn_table;
173
174     pr->c = NULL;
175     pr->backhandle = org->backhandle;
176
177     pr->s = s = sk_register(sock, (Plug) pr);
178     if ((err = sk_socket_error(s))) {
179         sfree(pr);
180         return err != NULL;
181     }
182
183     pr->c = new_sock_channel(org->backhandle, s);
184
185     strcpy(pr->hostname, org->hostname);
186     pr->port = org->port;
187     pr->throttled = pr->throttle_override = 0;
188     pr->ready = 0;
189     pr->waiting = NULL;
190
191     sk_set_private_ptr(s, pr);
192
193     if (pr->c == NULL) {
194         sfree(pr);
195         return 1;
196     } else {
197         /* asks to forward to the specified host/port for this */
198         ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding");
199     }
200
201     return 0;
202 }
203
204
205 /* Add a new forwarding from port -> desthost:destport
206  sets up a listener on the local machine on (srcaddr:)port
207  */
208 char *pfd_addforward(char *desthost, int destport, char *srcaddr, int port,
209                      void *backhandle)
210 {
211     static struct plug_function_table fn_table = {
212         pfd_closing,
213         pfd_receive,                   /* should not happen... */
214         pfd_sent,                      /* also should not happen */
215         pfd_accepting
216     };
217
218     char *err;
219     struct PFwdPrivate *pr;
220     Socket s;
221
222     /*
223      * Open socket.
224      */
225     pr = (struct PFwdPrivate *) smalloc(sizeof(struct PFwdPrivate));
226     pr->fn = &fn_table;
227     pr->c = NULL;
228     strcpy(pr->hostname, desthost);
229     pr->port = destport;
230     pr->throttled = pr->throttle_override = 0;
231     pr->ready = 0;
232     pr->waiting = NULL;
233     pr->backhandle = backhandle;
234
235     pr->s = s = new_listener(srcaddr, port, (Plug) pr, !cfg.lport_acceptall);
236     if ((err = sk_socket_error(s))) {
237         sfree(pr);
238         return err;
239     }
240
241     sk_set_private_ptr(s, pr);
242
243     return NULL;
244 }
245
246 void pfd_close(Socket s)
247 {
248     struct PFwdPrivate *pr;
249
250     if (!s)
251         return;
252
253     pr = (struct PFwdPrivate *) sk_get_private_ptr(s);
254
255     sfree(pr);
256
257     sk_close(s);
258 }
259
260 void pfd_unthrottle(Socket s)
261 {
262     struct PFwdPrivate *pr;
263     if (!s)
264         return;
265     pr = (struct PFwdPrivate *) sk_get_private_ptr(s);
266
267     pr->throttled = 0;
268     sk_set_frozen(s, pr->throttled || pr->throttle_override);
269 }
270
271 void pfd_override_throttle(Socket s, int enable)
272 {
273     struct PFwdPrivate *pr;
274     if (!s)
275         return;
276     pr = (struct PFwdPrivate *) sk_get_private_ptr(s);
277
278     pr->throttle_override = enable;
279     sk_set_frozen(s, pr->throttled || pr->throttle_override);
280 }
281
282 /*
283  * Called to send data down the raw connection.
284  */
285 int pfd_send(Socket s, char *data, int len)
286 {
287     if (s == NULL)
288         return 0;
289     return sk_write(s, data, len);
290 }
291
292
293 void pfd_confirm(Socket s)
294 {
295     struct PFwdPrivate *pr;
296
297     if (s == NULL)
298         return;
299
300     pr = (struct PFwdPrivate *) sk_get_private_ptr(s);
301     pr->ready = 1;
302     sk_set_frozen(s, 0);
303     sk_write(s, NULL, 0);
304 }