listen.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <auth.h>
  4. #define NAMELEN 64 /* reasonable upper limit for name elements */
  5. typedef struct Service Service;
  6. struct Service
  7. {
  8. char serv[NAMELEN]; /* name of the service */
  9. char remote[3*NAMELEN]; /* address of remote system */
  10. char prog[5*NAMELEN+1]; /* program to execute */
  11. };
  12. typedef struct Announce Announce;
  13. struct Announce
  14. {
  15. Announce *next;
  16. char *a;
  17. int announced;
  18. int whined;
  19. };
  20. int readstr(char*, char*, char*, int);
  21. void dolisten(char*, char*, int, char*, char*);
  22. void newcall(int, char*, char*, Service*);
  23. int findserv(char*, char*, Service*, char*);
  24. int getserv(char*, char*, Service*);
  25. void error(char*);
  26. void scandir(char*, char*, char*);
  27. void becomenone(void);
  28. void listendir(char*, char*, int);
  29. char listenlog[] = "listen";
  30. int quiet;
  31. char *cpu;
  32. char *proto;
  33. Announce *announcements;
  34. #define SEC 1000
  35. char *namespace;
  36. void
  37. usage(void)
  38. {
  39. error("usage: aux/listen [-q] [-n namespace] [-d servdir] [-t trustdir]"
  40. " [proto]");
  41. }
  42. /*
  43. * based on libthread's threadsetname, but drags in less library code.
  44. * actually just sets the arguments displayed.
  45. */
  46. static void
  47. procsetname(char *fmt, ...)
  48. {
  49. int fd;
  50. char *cmdname;
  51. char buf[128];
  52. va_list arg;
  53. va_start(arg, fmt);
  54. cmdname = vsmprint(fmt, arg);
  55. va_end(arg);
  56. if (cmdname == nil)
  57. return;
  58. snprint(buf, sizeof buf, "#p/%d/args", getpid());
  59. if((fd = open(buf, OWRITE)) >= 0){
  60. write(fd, cmdname, strlen(cmdname)+1);
  61. close(fd);
  62. }
  63. free(cmdname);
  64. }
  65. void
  66. main(int argc, char *argv[])
  67. {
  68. Service *s;
  69. char *protodir;
  70. char *trustdir;
  71. char *servdir;
  72. servdir = 0;
  73. trustdir = 0;
  74. proto = "tcp";
  75. quiet = 0;
  76. argv0 = argv[0];
  77. cpu = getenv("cputype");
  78. if(cpu == 0)
  79. error("can't get cputype");
  80. ARGBEGIN{
  81. case 'd':
  82. servdir = EARGF(usage());
  83. break;
  84. case 'q':
  85. quiet = 1;
  86. break;
  87. case 't':
  88. trustdir = EARGF(usage());
  89. break;
  90. case 'n':
  91. namespace = EARGF(usage());
  92. break;
  93. default:
  94. usage();
  95. }ARGEND;
  96. if(!servdir && !trustdir)
  97. servdir = "/bin/service";
  98. if(servdir && strlen(servdir) + NAMELEN >= sizeof(s->prog))
  99. error("service directory too long");
  100. if(trustdir && strlen(trustdir) + NAMELEN >= sizeof(s->prog))
  101. error("trusted service directory too long");
  102. switch(argc){
  103. case 1:
  104. proto = argv[0];
  105. break;
  106. case 0:
  107. break;
  108. default:
  109. usage();
  110. }
  111. syslog(0, listenlog, "started on %s", proto);
  112. protodir = proto;
  113. proto = strrchr(proto, '/');
  114. if(proto == 0)
  115. proto = protodir;
  116. else
  117. proto++;
  118. listendir(protodir, servdir, 0);
  119. listendir(protodir, trustdir, 1);
  120. /* command returns */
  121. exits(0);
  122. }
  123. static void
  124. dingdong(void*, char *msg)
  125. {
  126. if(strstr(msg, "alarm") != nil)
  127. noted(NCONT);
  128. noted(NDFLT);
  129. }
  130. void
  131. listendir(char *protodir, char *srvdir, int trusted)
  132. {
  133. int ctl, pid, start;
  134. char dir[40], err[128];
  135. Announce *a;
  136. Waitmsg *wm;
  137. if (srvdir == 0)
  138. return;
  139. /*
  140. * insulate ourselves from later
  141. * changing of console environment variables
  142. * erase privileged crypt state
  143. */
  144. switch(rfork(RFNOTEG|RFPROC|RFFDG|RFNOWAIT|RFENVG|RFNAMEG)) {
  145. case -1:
  146. error("fork");
  147. case 0:
  148. break;
  149. default:
  150. return;
  151. }
  152. procsetname("%s %s %s", protodir, srvdir, namespace);
  153. if (!trusted)
  154. becomenone();
  155. notify(dingdong);
  156. pid = getpid();
  157. for(;;){
  158. /*
  159. * loop through announcements and process trusted services in
  160. * invoker's ns and untrusted in none's.
  161. */
  162. scandir(proto, protodir, srvdir);
  163. for(a = announcements; a; a = a->next){
  164. if(a->announced > 0)
  165. continue;
  166. sleep((pid*10)%200);
  167. /* a process per service */
  168. switch(pid = rfork(RFFDG|RFPROC)){
  169. case -1:
  170. syslog(1, listenlog, "couldn't fork for %s", a->a);
  171. break;
  172. case 0:
  173. for(;;){
  174. ctl = announce(a->a, dir);
  175. if(ctl < 0) {
  176. errstr(err, sizeof err);
  177. if (!a->whined)
  178. syslog(1, listenlog,
  179. "giving up on %s: %r",
  180. a->a);
  181. if(strstr(err, "address in use")
  182. != nil)
  183. exits("addr-in-use");
  184. else
  185. exits("ctl");
  186. }
  187. dolisten(proto, dir, ctl, srvdir, a->a);
  188. close(ctl);
  189. }
  190. default:
  191. a->announced = pid;
  192. break;
  193. }
  194. }
  195. /* pick up any children that gave up and sleep for at least 60 seconds */
  196. start = time(0);
  197. alarm(60*1000);
  198. while((wm = wait()) != nil) {
  199. for(a = announcements; a; a = a->next)
  200. if(a->announced == wm->pid) {
  201. a->announced = 0;
  202. if (strstr(wm->msg, "addr-in-use") !=
  203. nil)
  204. /* don't fill log file */
  205. a->whined = 1;
  206. }
  207. free(wm);
  208. }
  209. alarm(0);
  210. start = 60 - (time(0)-start);
  211. if(start > 0)
  212. sleep(start*1000);
  213. }
  214. /* not reached */
  215. }
  216. /*
  217. * make a list of all services to announce for
  218. */
  219. void
  220. addannounce(char *str)
  221. {
  222. Announce *a, **l;
  223. /* look for duplicate */
  224. l = &announcements;
  225. for(a = announcements; a; a = a->next){
  226. if(strcmp(str, a->a) == 0)
  227. return;
  228. l = &a->next;
  229. }
  230. /* accept it */
  231. a = mallocz(sizeof(*a) + strlen(str) + 1, 1);
  232. if(a == 0)
  233. return;
  234. a->a = ((char*)a)+sizeof(*a);
  235. strcpy(a->a, str);
  236. a->announced = 0;
  237. *l = a;
  238. }
  239. /*
  240. * delete a service for announcement list
  241. */
  242. void
  243. delannounce(char *str)
  244. {
  245. Announce *a, **l;
  246. /* look for service */
  247. l = &announcements;
  248. for(a = announcements; a; a = a->next){
  249. if(strcmp(str, a->a) == 0)
  250. break;
  251. l = &a->next;
  252. }
  253. if (a == nil)
  254. return;
  255. *l = a->next; /* drop from the list */
  256. if (a->announced > 0)
  257. postnote(PNPROC, a->announced, "die");
  258. a->announced = 0;
  259. free(a);
  260. }
  261. static int
  262. ignore(char *srvdir, char *name)
  263. {
  264. int rv;
  265. char *file = smprint("%s/%s", srvdir, name);
  266. Dir *d = dirstat(file);
  267. rv = !d || d->length <= 0; /* ignore unless it's non-empty */
  268. free(d);
  269. free(file);
  270. return rv;
  271. }
  272. void
  273. scandir(char *proto, char *protodir, char *dname)
  274. {
  275. int fd, i, n, nlen;
  276. char *nm;
  277. char ds[128];
  278. Dir *db;
  279. fd = open(dname, OREAD);
  280. if(fd < 0)
  281. return;
  282. nlen = strlen(proto);
  283. while((n=dirread(fd, &db)) > 0){
  284. for(i=0; i<n; i++){
  285. nm = db[i].name;
  286. if(!(db[i].qid.type&QTDIR) &&
  287. strncmp(nm, proto, nlen) == 0) {
  288. snprint(ds, sizeof ds, "%s!*!%s", protodir,
  289. nm + nlen);
  290. if (ignore(dname, nm))
  291. delannounce(ds);
  292. else
  293. addannounce(ds);
  294. }
  295. }
  296. free(db);
  297. }
  298. close(fd);
  299. }
  300. void
  301. becomenone(void)
  302. {
  303. int fd;
  304. fd = open("#c/user", OWRITE);
  305. if(fd < 0 || write(fd, "none", strlen("none")) < 0)
  306. error("can't become none");
  307. close(fd);
  308. if(newns("none", namespace) < 0)
  309. error("can't build namespace");
  310. }
  311. void
  312. dolisten(char *proto, char *dir, int ctl, char *srvdir, char *dialstr)
  313. {
  314. Service s;
  315. char ndir[40];
  316. int nctl, data;
  317. procsetname("%s %s", dir, dialstr);
  318. for(;;){
  319. /*
  320. * wait for a call (or an error)
  321. */
  322. nctl = listen(dir, ndir);
  323. if(nctl < 0){
  324. if(!quiet)
  325. syslog(1, listenlog, "listen: %r");
  326. return;
  327. }
  328. /*
  329. * start a subprocess for the connection
  330. */
  331. switch(rfork(RFFDG|RFPROC|RFNOWAIT|RFENVG|RFNAMEG|RFNOTEG)){
  332. case -1:
  333. reject(nctl, ndir, "host overloaded");
  334. close(nctl);
  335. continue;
  336. case 0:
  337. /*
  338. * see if we know the service requested
  339. */
  340. memset(&s, 0, sizeof s);
  341. if(!findserv(proto, ndir, &s, srvdir)){
  342. if(!quiet)
  343. syslog(1, listenlog, "%s: unknown service '%s' from '%s': %r",
  344. proto, s.serv, s.remote);
  345. reject(nctl, ndir, "connection refused");
  346. exits(0);
  347. }
  348. data = accept(nctl, ndir);
  349. if(data < 0){
  350. syslog(1, listenlog, "can't open %s/data: %r", ndir);
  351. exits(0);
  352. }
  353. fprint(nctl, "keepalive");
  354. close(ctl);
  355. close(nctl);
  356. newcall(data, proto, ndir, &s);
  357. exits(0);
  358. default:
  359. close(nctl);
  360. break;
  361. }
  362. }
  363. }
  364. /*
  365. * look in the service directory for the service.
  366. * if the shell script or program is zero-length, ignore it,
  367. * thus providing a way to disable a service with a bind.
  368. */
  369. int
  370. findserv(char *proto, char *dir, Service *s, char *srvdir)
  371. {
  372. int rv;
  373. Dir *d;
  374. if(!getserv(proto, dir, s))
  375. return 0;
  376. snprint(s->prog, sizeof s->prog, "%s/%s", srvdir, s->serv);
  377. d = dirstat(s->prog);
  378. rv = d && d->length > 0; /* ignore unless it's non-empty */
  379. free(d);
  380. return rv;
  381. }
  382. /*
  383. * get the service name out of the local address
  384. */
  385. int
  386. getserv(char *proto, char *dir, Service *s)
  387. {
  388. char addr[128], *serv, *p;
  389. long n;
  390. readstr(dir, "remote", s->remote, sizeof(s->remote)-1);
  391. if(p = utfrune(s->remote, L'\n'))
  392. *p = '\0';
  393. n = readstr(dir, "local", addr, sizeof(addr)-1);
  394. if(n <= 0)
  395. return 0;
  396. if(p = utfrune(addr, L'\n'))
  397. *p = '\0';
  398. serv = utfrune(addr, L'!');
  399. if(!serv)
  400. serv = "login";
  401. else
  402. serv++;
  403. /*
  404. * disallow service names like
  405. * ../../usr/user/bin/rc/su
  406. */
  407. if(strlen(serv) +strlen(proto) >= NAMELEN || utfrune(serv, L'/') || *serv == '.')
  408. return 0;
  409. snprint(s->serv, sizeof s->serv, "%s%s", proto, serv);
  410. return 1;
  411. }
  412. char *
  413. remoteaddr(char *dir)
  414. {
  415. char buf[128], *p;
  416. int n, fd;
  417. snprint(buf, sizeof buf, "%s/remote", dir);
  418. fd = open(buf, OREAD);
  419. if(fd < 0)
  420. return strdup("");
  421. n = read(fd, buf, sizeof(buf));
  422. close(fd);
  423. if(n > 0){
  424. buf[n] = 0;
  425. p = strchr(buf, '!');
  426. if(p)
  427. *p = 0;
  428. return strdup(buf);
  429. }
  430. return strdup("");
  431. }
  432. void
  433. newcall(int fd, char *proto, char *dir, Service *s)
  434. {
  435. char data[4*NAMELEN];
  436. char *p;
  437. if(!quiet){
  438. if(dir != nil){
  439. p = remoteaddr(dir);
  440. syslog(0, listenlog, "%s call for %s on chan %s (%s)", proto, s->serv, dir, p);
  441. free(p);
  442. } else
  443. syslog(0, listenlog, "%s call for %s on chan %s", proto, s->serv, dir);
  444. }
  445. snprint(data, sizeof data, "%s/data", dir);
  446. bind(data, "/dev/cons", MREPL);
  447. dup(fd, 0);
  448. dup(fd, 1);
  449. dup(fd, 2);
  450. close(fd);
  451. /*
  452. * close all the fds
  453. */
  454. for(fd=3; fd<20; fd++)
  455. close(fd);
  456. execl(s->prog, s->prog, s->serv, proto, dir, nil);
  457. error(s->prog);
  458. }
  459. void
  460. error(char *s)
  461. {
  462. syslog(1, listenlog, "%s: %s: %r", proto, s);
  463. exits(0);
  464. }
  465. /*
  466. * read a string from a device
  467. */
  468. int
  469. readstr(char *dir, char *info, char *s, int len)
  470. {
  471. int n, fd;
  472. char buf[3*NAMELEN+4];
  473. snprint(buf, sizeof buf, "%s/%s", dir, info);
  474. fd = open(buf, OREAD);
  475. if(fd<0)
  476. return 0;
  477. n = read(fd, s, len-1);
  478. if(n<=0){
  479. close(fd);
  480. return -1;
  481. }
  482. s[n] = 0;
  483. close(fd);
  484. return n+1;
  485. }