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