• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
main(int argc,char ** argv)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
usage(void)97 usage(void)
98 {
99     fprintf(stderr, usage_line, progname);
100     exit(2);
101 }
102 
103 static void
main_thread(int port)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
create_thread(int conn,struct sockaddr_in * addr)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
init_python(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 *
service_thread(struct workorder * work)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
oprogname(void)268 oprogname(void)
269 {
270     int save = errno;
271     fprintf(stderr, "%s: ", progname);
272     errno = save;
273 }
274 
275 static void
run_interpreter(FILE * input,FILE * output)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
run_command(char * buffer,PyObject * globals)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
ps(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 }
371