5e1a8e27 |
1 | #include <windows.h> |
2 | #include <stdio.h> |
3 | #include <stdlib.h> |
4d331a77 |
4 | #ifndef AUTO_WINSOCK |
5 | #ifdef WINSOCK_TWO |
6 | #include <winsock2.h> |
7 | #else |
5e1a8e27 |
8 | #include <winsock.h> |
4d331a77 |
9 | #endif |
10 | #endif |
5e1a8e27 |
11 | |
12 | #include "putty.h" |
13 | |
14 | #ifndef FALSE |
15 | #define FALSE 0 |
16 | #endif |
17 | #ifndef TRUE |
18 | #define TRUE 1 |
19 | #endif |
20 | |
21 | static SOCKET s = INVALID_SOCKET; |
22 | |
5e1a8e27 |
23 | static void raw_size(void); |
24 | |
25 | static int sb_opt, sb_len; |
26 | static char *sb_buf = NULL; |
27 | static int sb_size = 0; |
28 | #define SB_DELTA 1024 |
29 | |
30 | static void try_write (void) { |
31 | while (outbuf_head != outbuf_reap) { |
32 | int end = (outbuf_reap < outbuf_head ? outbuf_head : OUTBUF_SIZE); |
33 | int len = end - outbuf_reap; |
34 | int ret; |
35 | |
36 | ret = send (s, outbuf+outbuf_reap, len, 0); |
37 | if (ret > 0) |
38 | outbuf_reap = (outbuf_reap + ret) & OUTBUF_MASK; |
39 | if (ret < len) |
40 | return; |
41 | } |
42 | } |
43 | |
44 | static void s_write (void *buf, int len) { |
45 | unsigned char *p = buf; |
46 | while (len--) { |
47 | int new_head = (outbuf_head + 1) & OUTBUF_MASK; |
48 | if (new_head != outbuf_reap) { |
49 | outbuf[outbuf_head] = *p++; |
50 | outbuf_head = new_head; |
51 | } |
52 | } |
53 | try_write(); |
54 | } |
55 | |
56 | static void c_write (char *buf, int len) { |
fe50e814 |
57 | from_backend(0, buf, len); |
5e1a8e27 |
58 | } |
59 | |
60 | /* |
61 | * Called to set up the raw connection. Will arrange for |
62 | * WM_NETEVENT messages to be passed to the specified window, whose |
63 | * window procedure should then call raw_msg(). |
64 | * |
65 | * Returns an error message, or NULL on success. |
66 | * |
67 | * Also places the canonical host name into `realhost'. |
68 | */ |
69 | static char *raw_init (HWND hwnd, char *host, int port, char **realhost) { |
70 | SOCKADDR_IN addr; |
71 | struct hostent *h; |
72 | unsigned long a; |
73 | |
74 | /* |
75 | * Try to find host. |
76 | */ |
77 | if ( (a = inet_addr(host)) == (unsigned long) INADDR_NONE) { |
78 | if ( (h = gethostbyname(host)) == NULL) |
79 | switch (WSAGetLastError()) { |
80 | case WSAENETDOWN: return "Network is down"; |
81 | case WSAHOST_NOT_FOUND: case WSANO_DATA: |
82 | return "Host does not exist"; |
83 | case WSATRY_AGAIN: return "Host not found"; |
84 | default: return "gethostbyname: unknown error"; |
85 | } |
86 | memcpy (&a, h->h_addr, sizeof(a)); |
87 | *realhost = h->h_name; |
88 | } else |
89 | *realhost = host; |
90 | a = ntohl(a); |
91 | |
92 | if (port < 0) |
93 | port = 23; /* default telnet port */ |
94 | |
95 | /* |
96 | * Open socket. |
97 | */ |
98 | s = socket(AF_INET, SOCK_STREAM, 0); |
99 | if (s == INVALID_SOCKET) |
100 | switch (WSAGetLastError()) { |
101 | case WSAENETDOWN: return "Network is down"; |
102 | case WSAEAFNOSUPPORT: return "TCP/IP support not present"; |
103 | default: return "socket(): unknown error"; |
104 | } |
105 | |
106 | /* |
107 | * Bind to local address. |
108 | */ |
109 | addr.sin_family = AF_INET; |
110 | addr.sin_addr.s_addr = htonl(INADDR_ANY); |
111 | addr.sin_port = htons(0); |
112 | if (bind (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) |
113 | switch (WSAGetLastError()) { |
114 | case WSAENETDOWN: return "Network is down"; |
115 | default: return "bind(): unknown error"; |
116 | } |
117 | |
118 | /* |
119 | * Connect to remote address. |
120 | */ |
121 | addr.sin_addr.s_addr = htonl(a); |
122 | addr.sin_port = htons((short)port); |
123 | if (connect (s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) |
124 | switch (WSAGetLastError()) { |
125 | case WSAENETDOWN: return "Network is down"; |
126 | case WSAECONNREFUSED: return "Connection refused"; |
127 | case WSAENETUNREACH: return "Network is unreachable"; |
128 | case WSAEHOSTUNREACH: return "No route to host"; |
129 | default: return "connect(): unknown error"; |
130 | } |
131 | |
4017be6d |
132 | if (hwnd && WSAAsyncSelect (s, hwnd, WM_NETEVENT, FD_READ | |
5e1a8e27 |
133 | FD_WRITE | FD_OOB | FD_CLOSE) == SOCKET_ERROR) |
134 | switch (WSAGetLastError()) { |
135 | case WSAENETDOWN: return "Network is down"; |
136 | default: return "WSAAsyncSelect(): unknown error"; |
137 | } |
138 | |
6f34e365 |
139 | /* |
140 | * We have no pre-session phase. |
141 | */ |
142 | begin_session(); |
143 | |
5e1a8e27 |
144 | return NULL; |
145 | } |
146 | |
147 | /* |
148 | * Process a WM_NETEVENT message. Will return 0 if the connection |
149 | * has closed, or <0 for a socket error. |
150 | */ |
151 | static int raw_msg (WPARAM wParam, LPARAM lParam) { |
152 | int ret; |
153 | char buf[256]; |
154 | |
8ce72d2c |
155 | /* |
156 | * Because reading less than the whole of the available pending |
157 | * data can generate an FD_READ event, we need to allow for the |
158 | * possibility that FD_READ may arrive with FD_CLOSE already in |
159 | * the queue; so it's possible that we can get here even with s |
160 | * invalid. If so, we return 1 and don't worry about it. |
161 | */ |
8d5de777 |
162 | if (s == INVALID_SOCKET) { |
163 | closesocket(s); |
164 | s = INVALID_SOCKET; |
8ce72d2c |
165 | return 1; |
8d5de777 |
166 | } |
5e1a8e27 |
167 | |
168 | if (WSAGETSELECTERROR(lParam) != 0) |
169 | return -WSAGETSELECTERROR(lParam); |
170 | |
171 | switch (WSAGETSELECTEVENT(lParam)) { |
172 | case FD_READ: |
8ce72d2c |
173 | case FD_CLOSE: |
5e1a8e27 |
174 | ret = recv(s, buf, sizeof(buf), 0); |
175 | if (ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK) |
176 | return 1; |
8d5de777 |
177 | if (ret < 0) { /* any _other_ error */ |
178 | closesocket(s); |
179 | s = INVALID_SOCKET; |
5e1a8e27 |
180 | return -10000-WSAGetLastError(); |
8d5de777 |
181 | } |
5e1a8e27 |
182 | if (ret == 0) { |
183 | s = INVALID_SOCKET; |
8ce72d2c |
184 | return 0; |
5e1a8e27 |
185 | } |
186 | c_write( buf, ret ); |
187 | return 1; |
188 | case FD_OOB: |
189 | do { |
190 | ret = recv(s, buf, sizeof(buf), 0); |
191 | c_write( buf, ret ); |
192 | } while (ret > 0); |
193 | do { |
194 | ret = recv(s, buf, 1, MSG_OOB); |
195 | } while (ret > 0); |
196 | if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK) |
197 | return -30000-WSAGetLastError(); |
198 | return 1; |
199 | case FD_WRITE: |
200 | if (outbuf_head != outbuf_reap) |
201 | try_write(); |
202 | return 1; |
5e1a8e27 |
203 | } |
204 | return 1; /* shouldn't happen, but WTF */ |
205 | } |
206 | |
207 | /* |
208 | * Called to send data down the raw connection. |
209 | */ |
210 | static void raw_send (char *buf, int len) { |
211 | |
212 | if (s == INVALID_SOCKET) |
213 | return; |
214 | |
215 | s_write( buf, len ); |
216 | } |
217 | |
218 | /* |
219 | * Called to set the size of the window |
220 | */ |
221 | static void raw_size(void) { |
222 | /* Do nothing! */ |
223 | return; |
224 | } |
225 | |
226 | /* |
227 | * Send raw special codes. |
228 | */ |
229 | static void raw_special (Telnet_Special code) { |
230 | /* Do nothing! */ |
231 | return; |
232 | } |
233 | |
8ccc75b0 |
234 | static SOCKET raw_socket(void) { return s; } |
235 | |
236 | static int raw_sendok(void) { return 1; } |
4017be6d |
237 | |
5e1a8e27 |
238 | Backend raw_backend = { |
239 | raw_init, |
240 | raw_msg, |
241 | raw_send, |
242 | raw_size, |
4017be6d |
243 | raw_special, |
8ccc75b0 |
244 | raw_socket, |
97db3be4 |
245 | raw_sendok, |
246 | 1 |
5e1a8e27 |
247 | }; |