|
1 /* A multi-threaded telnet-like server that gives a Python prompt. |
|
2 |
|
3 Usage: pysvr [port] |
|
4 |
|
5 For security reasons, it only accepts requests from the current host. |
|
6 This can still be insecure, but restricts violations from people who |
|
7 can log in on your machine. Use with caution! |
|
8 |
|
9 */ |
|
10 |
|
11 #include <stdio.h> |
|
12 #include <stdlib.h> |
|
13 #include <string.h> |
|
14 #include <ctype.h> |
|
15 #include <errno.h> |
|
16 |
|
17 #include <sys/types.h> |
|
18 #include <sys/socket.h> |
|
19 #include <netinet/in.h> |
|
20 |
|
21 #include <pthread.h> |
|
22 #include <getopt.h> |
|
23 |
|
24 /* XXX Umpfh. |
|
25 Python.h defines a typedef destructor, which conflicts with pthread.h. |
|
26 So Python.h must be included after pthread.h. */ |
|
27 |
|
28 #include "Python.h" |
|
29 |
|
30 extern int Py_VerboseFlag; |
|
31 |
|
32 #ifndef PORT |
|
33 #define PORT 4000 |
|
34 #endif |
|
35 |
|
36 struct workorder { |
|
37 int conn; |
|
38 struct sockaddr_in addr; |
|
39 }; |
|
40 |
|
41 /* Forward */ |
|
42 static void init_python(void); |
|
43 static void usage(void); |
|
44 static void oprogname(void); |
|
45 static void main_thread(int); |
|
46 static void create_thread(int, struct sockaddr_in *); |
|
47 static void *service_thread(struct workorder *); |
|
48 static void run_interpreter(FILE *, FILE *); |
|
49 static int run_command(char *, PyObject *); |
|
50 static void ps(void); |
|
51 |
|
52 static char *progname = "pysvr"; |
|
53 |
|
54 static PyThreadState *gtstate; |
|
55 |
|
56 main(int argc, char **argv) |
|
57 { |
|
58 int port = PORT; |
|
59 int c; |
|
60 |
|
61 if (argc > 0 && argv[0] != NULL && argv[0][0] != '\0') |
|
62 progname = argv[0]; |
|
63 |
|
64 while ((c = getopt(argc, argv, "v")) != EOF) { |
|
65 switch (c) { |
|
66 case 'v': |
|
67 Py_VerboseFlag++; |
|
68 break; |
|
69 default: |
|
70 usage(); |
|
71 } |
|
72 } |
|
73 |
|
74 if (optind < argc) { |
|
75 if (optind+1 < argc) { |
|
76 oprogname(); |
|
77 fprintf(stderr, "too many arguments\n"); |
|
78 usage(); |
|
79 } |
|
80 port = atoi(argv[optind]); |
|
81 if (port <= 0) { |
|
82 fprintf(stderr, "bad port (%s)\n", argv[optind]); |
|
83 usage(); |
|
84 } |
|
85 } |
|
86 |
|
87 main_thread(port); |
|
88 |
|
89 fprintf(stderr, "Bye.\n"); |
|
90 |
|
91 exit(0); |
|
92 } |
|
93 |
|
94 static char usage_line[] = "usage: %s [port]\n"; |
|
95 |
|
96 static void |
|
97 usage(void) |
|
98 { |
|
99 fprintf(stderr, usage_line, progname); |
|
100 exit(2); |
|
101 } |
|
102 |
|
103 static void |
|
104 main_thread(int port) |
|
105 { |
|
106 int sock, conn, size, i; |
|
107 struct sockaddr_in addr, clientaddr; |
|
108 |
|
109 sock = socket(PF_INET, SOCK_STREAM, 0); |
|
110 if (sock < 0) { |
|
111 oprogname(); |
|
112 perror("can't create socket"); |
|
113 exit(1); |
|
114 } |
|
115 |
|
116 #ifdef SO_REUSEADDR |
|
117 i = 1; |
|
118 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof i); |
|
119 #endif |
|
120 |
|
121 memset((char *)&addr, '\0', sizeof addr); |
|
122 addr.sin_family = AF_INET; |
|
123 addr.sin_port = htons(port); |
|
124 addr.sin_addr.s_addr = 0L; |
|
125 if (bind(sock, (struct sockaddr *)&addr, sizeof addr) < 0) { |
|
126 oprogname(); |
|
127 perror("can't bind socket to address"); |
|
128 exit(1); |
|
129 } |
|
130 |
|
131 if (listen(sock, 5) < 0) { |
|
132 oprogname(); |
|
133 perror("can't listen on socket"); |
|
134 exit(1); |
|
135 } |
|
136 |
|
137 fprintf(stderr, "Listening on port %d...\n", port); |
|
138 |
|
139 for (i = 0; ; i++) { |
|
140 size = sizeof clientaddr; |
|
141 memset((char *) &clientaddr, '\0', size); |
|
142 conn = accept(sock, (struct sockaddr *) &clientaddr, &size); |
|
143 if (conn < 0) { |
|
144 oprogname(); |
|
145 perror("can't accept connection from socket"); |
|
146 exit(1); |
|
147 } |
|
148 |
|
149 size = sizeof addr; |
|
150 memset((char *) &addr, '\0', size); |
|
151 if (getsockname(conn, (struct sockaddr *)&addr, &size) < 0) { |
|
152 oprogname(); |
|
153 perror("can't get socket name of connection"); |
|
154 exit(1); |
|
155 } |
|
156 if (clientaddr.sin_addr.s_addr != addr.sin_addr.s_addr) { |
|
157 oprogname(); |
|
158 perror("connection from non-local host refused"); |
|
159 fprintf(stderr, "(addr=%lx, clientaddr=%lx)\n", |
|
160 ntohl(addr.sin_addr.s_addr), |
|
161 ntohl(clientaddr.sin_addr.s_addr)); |
|
162 close(conn); |
|
163 continue; |
|
164 } |
|
165 if (i == 4) { |
|
166 close(conn); |
|
167 break; |
|
168 } |
|
169 create_thread(conn, &clientaddr); |
|
170 } |
|
171 |
|
172 close(sock); |
|
173 |
|
174 if (gtstate) { |
|
175 PyEval_AcquireThread(gtstate); |
|
176 gtstate = NULL; |
|
177 Py_Finalize(); |
|
178 /* And a second time, just because we can. */ |
|
179 Py_Finalize(); /* This should be harmless. */ |
|
180 } |
|
181 exit(0); |
|
182 } |
|
183 |
|
184 static void |
|
185 create_thread(int conn, struct sockaddr_in *addr) |
|
186 { |
|
187 struct workorder *work; |
|
188 pthread_t tdata; |
|
189 |
|
190 work = malloc(sizeof(struct workorder)); |
|
191 if (work == NULL) { |
|
192 oprogname(); |
|
193 fprintf(stderr, "out of memory for thread.\n"); |
|
194 close(conn); |
|
195 return; |
|
196 } |
|
197 work->conn = conn; |
|
198 work->addr = *addr; |
|
199 |
|
200 init_python(); |
|
201 |
|
202 if (pthread_create(&tdata, NULL, (void *)service_thread, work) < 0) { |
|
203 oprogname(); |
|
204 perror("can't create new thread"); |
|
205 close(conn); |
|
206 return; |
|
207 } |
|
208 |
|
209 if (pthread_detach(tdata) < 0) { |
|
210 oprogname(); |
|
211 perror("can't detach from thread"); |
|
212 } |
|
213 } |
|
214 |
|
215 static PyThreadState *the_tstate; |
|
216 static PyInterpreterState *the_interp; |
|
217 static PyObject *the_builtins; |
|
218 |
|
219 static void |
|
220 init_python(void) |
|
221 { |
|
222 if (gtstate) |
|
223 return; |
|
224 Py_Initialize(); /* Initialize the interpreter */ |
|
225 PyEval_InitThreads(); /* Create (and acquire) the interpreter lock */ |
|
226 gtstate = PyEval_SaveThread(); /* Release the thread state */ |
|
227 } |
|
228 |
|
229 static void * |
|
230 service_thread(struct workorder *work) |
|
231 { |
|
232 FILE *input, *output; |
|
233 |
|
234 fprintf(stderr, "Start thread for connection %d.\n", work->conn); |
|
235 |
|
236 ps(); |
|
237 |
|
238 input = fdopen(work->conn, "r"); |
|
239 if (input == NULL) { |
|
240 oprogname(); |
|
241 perror("can't create input stream"); |
|
242 goto done; |
|
243 } |
|
244 |
|
245 output = fdopen(work->conn, "w"); |
|
246 if (output == NULL) { |
|
247 oprogname(); |
|
248 perror("can't create output stream"); |
|
249 fclose(input); |
|
250 goto done; |
|
251 } |
|
252 |
|
253 setvbuf(input, NULL, _IONBF, 0); |
|
254 setvbuf(output, NULL, _IONBF, 0); |
|
255 |
|
256 run_interpreter(input, output); |
|
257 |
|
258 fclose(input); |
|
259 fclose(output); |
|
260 |
|
261 done: |
|
262 fprintf(stderr, "End thread for connection %d.\n", work->conn); |
|
263 close(work->conn); |
|
264 free(work); |
|
265 } |
|
266 |
|
267 static void |
|
268 oprogname(void) |
|
269 { |
|
270 int save = errno; |
|
271 fprintf(stderr, "%s: ", progname); |
|
272 errno = save; |
|
273 } |
|
274 |
|
275 static void |
|
276 run_interpreter(FILE *input, FILE *output) |
|
277 { |
|
278 PyThreadState *tstate; |
|
279 PyObject *new_stdin, *new_stdout; |
|
280 PyObject *mainmod, *globals; |
|
281 char buffer[1000]; |
|
282 char *p, *q; |
|
283 int n, end; |
|
284 |
|
285 PyEval_AcquireLock(); |
|
286 tstate = Py_NewInterpreter(); |
|
287 if (tstate == NULL) { |
|
288 fprintf(output, "Sorry -- can't create an interpreter\n"); |
|
289 return; |
|
290 } |
|
291 |
|
292 mainmod = PyImport_AddModule("__main__"); |
|
293 globals = PyModule_GetDict(mainmod); |
|
294 Py_INCREF(globals); |
|
295 |
|
296 new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL); |
|
297 new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL); |
|
298 |
|
299 PySys_SetObject("stdin", new_stdin); |
|
300 PySys_SetObject("stdout", new_stdout); |
|
301 PySys_SetObject("stderr", new_stdout); |
|
302 |
|
303 for (n = 1; !PyErr_Occurred(); n++) { |
|
304 Py_BEGIN_ALLOW_THREADS |
|
305 fprintf(output, "%d> ", n); |
|
306 p = fgets(buffer, sizeof buffer, input); |
|
307 Py_END_ALLOW_THREADS |
|
308 |
|
309 if (p == NULL) |
|
310 break; |
|
311 if (p[0] == '\377' && p[1] == '\354') |
|
312 break; |
|
313 |
|
314 q = strrchr(p, '\r'); |
|
315 if (q && q[1] == '\n' && q[2] == '\0') { |
|
316 *q++ = '\n'; |
|
317 *q++ = '\0'; |
|
318 } |
|
319 |
|
320 while (*p && isspace(*p)) |
|
321 p++; |
|
322 if (p[0] == '#' || p[0] == '\0') |
|
323 continue; |
|
324 |
|
325 end = run_command(buffer, globals); |
|
326 if (end < 0) |
|
327 PyErr_Print(); |
|
328 |
|
329 if (end) |
|
330 break; |
|
331 } |
|
332 |
|
333 Py_XDECREF(globals); |
|
334 Py_XDECREF(new_stdin); |
|
335 Py_XDECREF(new_stdout); |
|
336 |
|
337 Py_EndInterpreter(tstate); |
|
338 PyEval_ReleaseLock(); |
|
339 |
|
340 fprintf(output, "Goodbye!\n"); |
|
341 } |
|
342 |
|
343 static int |
|
344 run_command(char *buffer, PyObject *globals) |
|
345 { |
|
346 PyObject *m, *d, *v; |
|
347 fprintf(stderr, "run_command: %s", buffer); |
|
348 if (strchr(buffer, '\n') == NULL) |
|
349 fprintf(stderr, "\n"); |
|
350 v = PyRun_String(buffer, Py_single_input, globals, globals); |
|
351 if (v == NULL) { |
|
352 if (PyErr_Occurred() == PyExc_SystemExit) { |
|
353 PyErr_Clear(); |
|
354 return 1; |
|
355 } |
|
356 PyErr_Print(); |
|
357 return 0; |
|
358 } |
|
359 Py_DECREF(v); |
|
360 return 0; |
|
361 } |
|
362 |
|
363 static void |
|
364 ps(void) |
|
365 { |
|
366 char buffer[100]; |
|
367 PyOS_snprintf(buffer, sizeof(buffer), |
|
368 "ps -l -p %d </dev/null | sed 1d\n", getpid()); |
|
369 system(buffer); |
|
370 } |