[2] | 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 {
|
---|
[391] | 37 | int conn;
|
---|
| 38 | struct sockaddr_in addr;
|
---|
[2] | 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 | {
|
---|
[391] | 58 | int port = PORT;
|
---|
| 59 | int c;
|
---|
[2] | 60 |
|
---|
[391] | 61 | if (argc > 0 && argv[0] != NULL && argv[0][0] != '\0')
|
---|
| 62 | progname = argv[0];
|
---|
[2] | 63 |
|
---|
[391] | 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 | }
|
---|
[2] | 73 |
|
---|
[391] | 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 | }
|
---|
[2] | 86 |
|
---|
[391] | 87 | main_thread(port);
|
---|
[2] | 88 |
|
---|
[391] | 89 | fprintf(stderr, "Bye.\n");
|
---|
[2] | 90 |
|
---|
[391] | 91 | exit(0);
|
---|
[2] | 92 | }
|
---|
| 93 |
|
---|
| 94 | static char usage_line[] = "usage: %s [port]\n";
|
---|
| 95 |
|
---|
| 96 | static void
|
---|
| 97 | usage(void)
|
---|
| 98 | {
|
---|
[391] | 99 | fprintf(stderr, usage_line, progname);
|
---|
| 100 | exit(2);
|
---|
[2] | 101 | }
|
---|
| 102 |
|
---|
| 103 | static void
|
---|
| 104 | main_thread(int port)
|
---|
| 105 | {
|
---|
[391] | 106 | int sock, conn, size, i;
|
---|
| 107 | struct sockaddr_in addr, clientaddr;
|
---|
[2] | 108 |
|
---|
[391] | 109 | sock = socket(PF_INET, SOCK_STREAM, 0);
|
---|
| 110 | if (sock < 0) {
|
---|
| 111 | oprogname();
|
---|
| 112 | perror("can't create socket");
|
---|
| 113 | exit(1);
|
---|
| 114 | }
|
---|
[2] | 115 |
|
---|
| 116 | #ifdef SO_REUSEADDR
|
---|
[391] | 117 | i = 1;
|
---|
| 118 | setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof i);
|
---|
[2] | 119 | #endif
|
---|
| 120 |
|
---|
[391] | 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 | }
|
---|
[2] | 130 |
|
---|
[391] | 131 | if (listen(sock, 5) < 0) {
|
---|
| 132 | oprogname();
|
---|
| 133 | perror("can't listen on socket");
|
---|
| 134 | exit(1);
|
---|
| 135 | }
|
---|
[2] | 136 |
|
---|
[391] | 137 | fprintf(stderr, "Listening on port %d...\n", port);
|
---|
[2] | 138 |
|
---|
[391] | 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 | }
|
---|
[2] | 148 |
|
---|
[391] | 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 | }
|
---|
[2] | 171 |
|
---|
[391] | 172 | close(sock);
|
---|
[2] | 173 |
|
---|
[391] | 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);
|
---|
[2] | 182 | }
|
---|
| 183 |
|
---|
| 184 | static void
|
---|
| 185 | create_thread(int conn, struct sockaddr_in *addr)
|
---|
| 186 | {
|
---|
[391] | 187 | struct workorder *work;
|
---|
| 188 | pthread_t tdata;
|
---|
[2] | 189 |
|
---|
[391] | 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;
|
---|
[2] | 199 |
|
---|
[391] | 200 | init_python();
|
---|
[2] | 201 |
|
---|
[391] | 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 | }
|
---|
[2] | 208 |
|
---|
[391] | 209 | if (pthread_detach(tdata) < 0) {
|
---|
| 210 | oprogname();
|
---|
| 211 | perror("can't detach from thread");
|
---|
| 212 | }
|
---|
[2] | 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 | {
|
---|
[391] | 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 */
|
---|
[2] | 227 | }
|
---|
| 228 |
|
---|
| 229 | static void *
|
---|
| 230 | service_thread(struct workorder *work)
|
---|
| 231 | {
|
---|
[391] | 232 | FILE *input, *output;
|
---|
[2] | 233 |
|
---|
[391] | 234 | fprintf(stderr, "Start thread for connection %d.\n", work->conn);
|
---|
[2] | 235 |
|
---|
[391] | 236 | ps();
|
---|
[2] | 237 |
|
---|
[391] | 238 | input = fdopen(work->conn, "r");
|
---|
| 239 | if (input == NULL) {
|
---|
| 240 | oprogname();
|
---|
| 241 | perror("can't create input stream");
|
---|
| 242 | goto done;
|
---|
| 243 | }
|
---|
[2] | 244 |
|
---|
[391] | 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 | }
|
---|
[2] | 252 |
|
---|
[391] | 253 | setvbuf(input, NULL, _IONBF, 0);
|
---|
| 254 | setvbuf(output, NULL, _IONBF, 0);
|
---|
[2] | 255 |
|
---|
[391] | 256 | run_interpreter(input, output);
|
---|
[2] | 257 |
|
---|
[391] | 258 | fclose(input);
|
---|
| 259 | fclose(output);
|
---|
[2] | 260 |
|
---|
| 261 | done:
|
---|
[391] | 262 | fprintf(stderr, "End thread for connection %d.\n", work->conn);
|
---|
| 263 | close(work->conn);
|
---|
| 264 | free(work);
|
---|
[2] | 265 | }
|
---|
| 266 |
|
---|
| 267 | static void
|
---|
| 268 | oprogname(void)
|
---|
| 269 | {
|
---|
[391] | 270 | int save = errno;
|
---|
| 271 | fprintf(stderr, "%s: ", progname);
|
---|
| 272 | errno = save;
|
---|
[2] | 273 | }
|
---|
| 274 |
|
---|
| 275 | static void
|
---|
| 276 | run_interpreter(FILE *input, FILE *output)
|
---|
| 277 | {
|
---|
[391] | 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;
|
---|
[2] | 284 |
|
---|
[391] | 285 | PyEval_AcquireLock();
|
---|
| 286 | tstate = Py_NewInterpreter();
|
---|
| 287 | if (tstate == NULL) {
|
---|
| 288 | fprintf(output, "Sorry -- can't create an interpreter\n");
|
---|
| 289 | return;
|
---|
| 290 | }
|
---|
[2] | 291 |
|
---|
[391] | 292 | mainmod = PyImport_AddModule("__main__");
|
---|
| 293 | globals = PyModule_GetDict(mainmod);
|
---|
| 294 | Py_INCREF(globals);
|
---|
[2] | 295 |
|
---|
[391] | 296 | new_stdin = PyFile_FromFile(input, "<socket-in>", "r", NULL);
|
---|
| 297 | new_stdout = PyFile_FromFile(output, "<socket-out>", "w", NULL);
|
---|
[2] | 298 |
|
---|
[391] | 299 | PySys_SetObject("stdin", new_stdin);
|
---|
| 300 | PySys_SetObject("stdout", new_stdout);
|
---|
| 301 | PySys_SetObject("stderr", new_stdout);
|
---|
[2] | 302 |
|
---|
[391] | 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
|
---|
[2] | 308 |
|
---|
[391] | 309 | if (p == NULL)
|
---|
| 310 | break;
|
---|
| 311 | if (p[0] == '\377' && p[1] == '\354')
|
---|
| 312 | break;
|
---|
[2] | 313 |
|
---|
[391] | 314 | q = strrchr(p, '\r');
|
---|
| 315 | if (q && q[1] == '\n' && q[2] == '\0') {
|
---|
| 316 | *q++ = '\n';
|
---|
| 317 | *q++ = '\0';
|
---|
| 318 | }
|
---|
[2] | 319 |
|
---|
[391] | 320 | while (*p && isspace(*p))
|
---|
| 321 | p++;
|
---|
| 322 | if (p[0] == '#' || p[0] == '\0')
|
---|
| 323 | continue;
|
---|
[2] | 324 |
|
---|
[391] | 325 | end = run_command(buffer, globals);
|
---|
| 326 | if (end < 0)
|
---|
| 327 | PyErr_Print();
|
---|
[2] | 328 |
|
---|
[391] | 329 | if (end)
|
---|
| 330 | break;
|
---|
| 331 | }
|
---|
[2] | 332 |
|
---|
[391] | 333 | Py_XDECREF(globals);
|
---|
| 334 | Py_XDECREF(new_stdin);
|
---|
| 335 | Py_XDECREF(new_stdout);
|
---|
[2] | 336 |
|
---|
[391] | 337 | Py_EndInterpreter(tstate);
|
---|
| 338 | PyEval_ReleaseLock();
|
---|
[2] | 339 |
|
---|
[391] | 340 | fprintf(output, "Goodbye!\n");
|
---|
[2] | 341 | }
|
---|
| 342 |
|
---|
| 343 | static int
|
---|
| 344 | run_command(char *buffer, PyObject *globals)
|
---|
| 345 | {
|
---|
[391] | 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;
|
---|
[2] | 361 | }
|
---|
| 362 |
|
---|
| 363 | static void
|
---|
| 364 | ps(void)
|
---|
| 365 | {
|
---|
[391] | 366 | char buffer[100];
|
---|
| 367 | PyOS_snprintf(buffer, sizeof(buffer),
|
---|
| 368 | "ps -l -p %d </dev/null | sed 1d\n", getpid());
|
---|
| 369 | system(buffer);
|
---|
[2] | 370 | }
|
---|