#include "links.h" #ifndef DISABLE_SMB #define SMBCLIENT 0 #define SMBC 1 #define N_CLIENTS 2 static int smb_client = 0; #define CLIENT_NOT_FOUND_STRING "client not found" struct smb_connection_info { int client; int list; int cl; int ntext; unsigned char text[1]; }; static void smb_got_data(void *); static void smb_got_text(void *); static void end_smb_connection(struct connection *); void smb_func(struct connection *c) { int i; int po[2]; int pe[2]; unsigned char *host, *user, *pass, *port, *data1, *data, *share, *dir; int datal; unsigned char *p; pid_t r; int rs; struct smb_connection_info *si; si = mem_alloc(sizeof(struct smb_connection_info) + 2); memset(si, 0, sizeof(struct smb_connection_info)); c->info = si; si->client = smb_client; host = get_host_name(c->url); if (!host) { setcstate(c, S_INTERNAL); abort_connection(c); return; } if (!(user = get_user_name(c->url))) user = stracpy(cast_uchar ""); if (!(pass = get_pass(c->url))) pass = stracpy(cast_uchar ""); if (!(port = get_port_str(c->url))) port = stracpy(cast_uchar ""); if (!(data1 = get_url_data(c->url))) data1 = cast_uchar ""; data = init_str(), datal = 0; add_conv_str(&data, &datal, data1, (int)strlen(cast_const_char data1), -2); for (i = 0; data[i]; i++) if (data[i] < 32 || data[i] == ';' || (data[i] == '"' && smb_client == SMBCLIENT)) { /* ';' shouldn't cause security problems but samba doesn't like it */ /* '"' is allowed for smbc */ mem_free(host); mem_free(port); mem_free(user); mem_free(pass); mem_free(data); setcstate(c, S_BAD_URL); abort_connection(c); return; } if ((p = cast_uchar strchr(cast_const_char data, '/'))) share = memacpy(data, p - data), dir = p + 1; else if (*data) { if (!c->cache) { if (get_connection_cache_entry(c)) { mem_free(host); mem_free(port); mem_free(user); mem_free(pass); mem_free(data); setcstate(c, S_OUT_OF_MEM); abort_connection(c); return; } c->cache->refcount--; } if (c->cache->redirect) mem_free(c->cache->redirect); c->cache->redirect = stracpy(c->url); add_to_strn(&c->cache->redirect, cast_uchar "/"); c->cache->incomplete = 0; mem_free(host); mem_free(port); mem_free(user); mem_free(pass); mem_free(data); setcstate(c, S__OK); abort_connection(c); return; } else share = stracpy(cast_uchar ""), dir = cast_uchar ""; if (!*share) si->list = 1; else if (!*dir || dir[strlen(cast_const_char dir) - 1] == '/' || dir[strlen(cast_const_char dir) - 1] == '\\') si->list = 2; if (c_pipe(po)) { int err = errno; mem_free(host); mem_free(port); mem_free(user); mem_free(pass); mem_free(share); mem_free(data); setcstate(c, get_error_from_errno(err)); abort_connection(c); return; } if (c_pipe(pe)) { int err = errno; mem_free(host); mem_free(port); mem_free(user); mem_free(pass); mem_free(share); mem_free(data); EINTRLOOP(rs, close(po[0])); EINTRLOOP(rs, close(po[1])); setcstate(c, get_error_from_errno(err)); abort_connection(c); return; } c->from = 0; EINTRLOOP(r, fork()); if (r == -1) { int err = errno; mem_free(host); mem_free(port); mem_free(user); mem_free(pass); mem_free(share); mem_free(data); EINTRLOOP(rs, close(po[0])); EINTRLOOP(rs, close(po[1])); EINTRLOOP(rs, close(pe[0])); EINTRLOOP(rs, close(pe[1])); setcstate(c, get_error_from_errno(err)); retry_connection(c); return; } if (!r) { int n; unsigned char *v[32]; unsigned char *uphp; close_fork_tty(); EINTRLOOP(rs, close(1)); if (si->list) EINTRLOOP(rs, dup2(pe[1], 1)); else EINTRLOOP(rs, dup2(po[1], 1)); EINTRLOOP(rs, close(2)); EINTRLOOP(rs, dup2(pe[1], 2)); EINTRLOOP(rs, close(0)); EINTRLOOP(rs, open("/dev/null", O_RDONLY)); EINTRLOOP(rs, close(po[0])); EINTRLOOP(rs, close(pe[0])); EINTRLOOP(rs, close(po[1])); EINTRLOOP(rs, close(pe[1])); n = 0; switch (si->client) { case SMBCLIENT: v[n++] = cast_uchar "smbclient"; if (!*share) { v[n++] = cast_uchar "-L"; v[n++] = host; } else { unsigned char *s = stracpy(cast_uchar "//"); add_to_strn(&s, host); add_to_strn(&s, cast_uchar "/"); add_to_strn(&s, share); v[n++] = s; if (*pass && !*user) { v[n++] = pass; } } v[n++] = cast_uchar "-N"; v[n++] = cast_uchar "-E"; if (*port) { v[n++] = cast_uchar "-p"; v[n++] = port; } if (*user) { v[n++] = cast_uchar "-U"; if (!*pass) { v[n++] = user; } else { unsigned char *s = stracpy(user); add_to_strn(&s, cast_uchar "%"); add_to_strn(&s, pass); v[n++] = s; } } if (*share) { if (!*dir || dir[strlen(cast_const_char dir) - 1] == '/' || dir[strlen(cast_const_char dir) - 1] == '\\') { if (*dir) { v[n++] = cast_uchar "-D"; v[n++] = dir; } v[n++] = cast_uchar "-c"; v[n++] = cast_uchar "ls"; } else { unsigned char *ss; unsigned char *s = stracpy(cast_uchar "get \""); add_to_strn(&s, dir); add_to_strn(&s, cast_uchar "\" -"); while ((ss = cast_uchar strchr(cast_const_char s, '/'))) *ss = '\\'; v[n++] = cast_uchar "-c"; v[n++] = s; } } break; case SMBC: v[n++] = cast_uchar "smbc"; uphp = stracpy(cast_uchar ""); if (*user) { add_to_strn(&uphp, user); if (*pass) { add_to_strn(&uphp, cast_uchar ":"); add_to_strn(&uphp, pass); } add_to_strn(&uphp, cast_uchar "@"); } add_to_strn(&uphp, host); if (*port) { add_to_strn(&uphp, cast_uchar ":"); add_to_strn(&uphp, port); } if (!*share) { v[n++] = cast_uchar "-L"; v[n++] = uphp; } else { add_to_strn(&uphp, cast_uchar "/"); add_to_strn(&uphp, share); if (!*dir || dir[strlen(cast_const_char dir) - 1] == '/' || dir[strlen(cast_const_char dir) - 1] == '\\') { add_to_strn(&uphp, cast_uchar "/"); add_to_strn(&uphp, dir); v[n++] = uphp; v[n++] = cast_uchar "-c"; v[n++] = cast_uchar "ls"; } else { unsigned char *d = init_str(); int dl = 0; unsigned char *dp = dir; v[n++] = uphp; v[n++] = cast_uchar "-c"; add_to_str(&d, &dl, cast_uchar "pipe cat "); while (*dp) { if (*dp <= ' ' || *dp == '\\' || *dp == '"' || *dp == '\'' || *dp == '*' || *dp == '?') add_chr_to_str(&d, &dl, '\\'); add_chr_to_str(&d, &dl, *dp); dp++; } v[n++] = d; } } break; default: internal_error("unsupported smb client"); } v[n++] = NULL; EINTRLOOP(rs, execvp(cast_const_char v[0], (void *)v)); hard_write(2, cast_uchar CLIENT_NOT_FOUND_STRING, (int)strlen(CLIENT_NOT_FOUND_STRING)); _exit(1); } c->pid = r; mem_free(host); mem_free(port); mem_free(user); mem_free(pass); mem_free(share); mem_free(data); c->sock1 = po[0]; c->sock2 = pe[0]; EINTRLOOP(rs, close(po[1])); EINTRLOOP(rs, close(pe[1])); set_handlers(po[0], smb_got_data, NULL, c); set_handlers(pe[0], smb_got_text, NULL, c); setcstate(c, S_CONN); } static int smbc_get_num(unsigned char *text, int *ptr, off_t *res) { off_t num; int dec, dec_order, unit; int was_digit; int i = *ptr; const off_t max_off_t = (((off_t)1 << ((sizeof(off_t) * 8 - 2))) - 1) * 2 + 1; while (text[i] == ' ' || text[i] == '\t') i++; was_digit = 0; num = 0; while (text[i] >= '0' && text[i] <= '9') { if (num >= max_off_t / 10) return -1; num = num * 10 + text[i] - '0'; i++; was_digit = 1; } dec = 0; dec_order = 1; if (text[i] == '.') { i++; while (text[i] >= '0' && text[i] <= '9') { if (dec_order < 1000000) { dec = dec * 10 + text[i] - '0'; dec_order *= 10; } i++; was_digit = 1; } } if (!was_digit) return -1; if (upcase(text[i]) == 'B') unit = 1; else if (upcase(text[i]) == 'K') unit = 1 << 10; else if (upcase(text[i]) == 'M') unit = 1 << 20; else if (upcase(text[i]) == 'G') unit = 1 << 30; else return -1; i++; *ptr = i; if (num >= max_off_t / unit) return -1; *res = num * unit + (off_t)((double)dec * ((double)unit / (double)dec_order)); return 0; } static void smb_read_text(struct connection *c, int sock) { int r; struct smb_connection_info *si = c->info; if ((size_t)sizeof(struct smb_connection_info) + si->ntext + page_size + 2 > (size_t)MAXINT) overalloc(); si = mem_realloc(si, sizeof(struct smb_connection_info) + si->ntext + page_size + 2); c->info = si; EINTRLOOP(r, (int)read(sock, si->text + si->ntext, page_size)); if (r == -1) { setcstate(c, get_error_from_errno(errno)); retry_connection(c); return; } if (r == 0) { if (!si->cl) { si->cl = 1; set_handlers(sock, NULL, NULL, NULL); return; } end_smb_connection(c); return; } si->ntext += r; if (!c->from) setcstate(c, S_GETH); if (c->from && si->client == SMBC) { int lasti = 0; int i = 0; si->text[si->ntext] = 0; for (i = 0; si->ntext - i > 7; i++) { nexti: if ((si->text[i] == '\n' || si->text[i] == '\r') && (si->text[i + 1] == ' ' || (si->text[i + 1] >= '0' && si->text[i + 1] <= '9')) && ((si->text[i + 2] == ' ' && si->text[i + 1] == ' ') || (si->text[i + 2] >= '0' && si->text[i + 2] <= '9')) && (si->text[i + 3] >= '0' && si->text[i + 3] <= '9') && si->text[i + 4] == '%' && si->text[i + 5] == ' ' && si->text[i + 6] == '[') { off_t position, total = 0; /* against warning */ i += 7; while (si->text[i] != ']') { if (!si->text[i] || si->text[i] == '\n' || si->text[i] == '\r') { goto nexti; } i++; } i++; if (smbc_get_num(si->text, &i, &position)) { goto nexti; } while (si->text[i] == ' ' || si->text[i] == '\t') i++; if (si->text[i] != '/') { goto nexti; } i++; if (smbc_get_num(si->text, &i, &total)) { goto nexti; } if (total < c->from) total = c->from; c->est_length = total; lasti = i; } } if (lasti) memmove(si->text, si->text + lasti, si->ntext -= lasti); } } static void smb_got_data(void *c_) { struct connection *c = (struct connection *)c_; struct smb_connection_info *si = c->info; unsigned char *buffer = mem_alloc(page_size); int r; int a; if (si->list) { smb_read_text(c, c->sock1); mem_free(buffer); return; } EINTRLOOP(r, (int)read(c->sock1, buffer, page_size)); if (r == -1) { setcstate(c, get_error_from_errno(errno)); retry_connection(c); mem_free(buffer); return; } if (r == 0) { mem_free(buffer); if (!si->cl) { si->cl = 1; set_handlers(c->sock1, NULL, NULL, NULL); return; } end_smb_connection(c); return; } setcstate(c, S_TRANS); if (!c->cache) { if (get_connection_cache_entry(c)) { setcstate(c, S_OUT_OF_MEM); abort_connection(c); mem_free(buffer); return; } c->cache->refcount--; } if ((off_t)(0UL + c->from + r) < 0) { setcstate(c, S_LARGE_FILE); abort_connection(c); mem_free(buffer); return; } c->received += r; a = add_fragment(c->cache, c->from, buffer, r); if (a < 0) { setcstate(c, a); abort_connection(c); mem_free(buffer); return; } if (a == 1) c->tries = 0; c->from += r; mem_free(buffer); } static void smb_got_text(void *c_) { struct connection *c = (struct connection *)c_; smb_read_text(c, c->sock2); } static void end_smb_connection(struct connection *c) { struct smb_connection_info *si = c->info; if (!c->cache) { if (get_connection_cache_entry(c)) { setcstate(c, S_OUT_OF_MEM); abort_connection(c); return; } c->cache->refcount--; } if (!c->from) { int sdir; if (si->ntext && si->text[si->ntext - 1] != '\n') si->text[si->ntext++] = '\n'; si->text[si->ntext] = 0; if (!strcmp(cast_const_char si->text, CLIENT_NOT_FOUND_STRING "\n")) { setcstate(c, S_NO_SMB_CLIENT); if (++si->client < N_CLIENTS) { if (si->client > smb_client) smb_client = si->client; c->tries = -1; retry_connection(c); } else { smb_client = 0; abort_connection(c); } return; } sdir = 0; if (si->client == SMBC) { unsigned char *st = si->text; if (!memcmp(st, "ServerName", 10) && strchr(cast_const_char st, '\n')) st = cast_uchar strchr(cast_const_char st, '\n') + 1; if (!memcmp(st, "Logged", 6) && strchr(cast_const_char st, '\n')) st = cast_uchar strchr(cast_const_char st, '\n') + 1; if (!strstr(cast_const_char st, "ERR")) sdir = 1; } if (!si->list && *c->url && c->url[strlen(cast_const_char c->url) - 1] != '/' && c->url[strlen(cast_const_char c->url) - 1] != '\\' && (strstr(cast_const_char si->text, "NT_STATUS_FILE_IS_A_DIRECTORY") || strstr(cast_const_char si->text, "NT_STATUS_ACCESS_DENIED") || strstr(cast_const_char si->text, "ERRbadfile") || sdir)) { if (c->cache->redirect) mem_free(c->cache->redirect); c->cache->redirect = stracpy(c->url); add_to_strn(&c->cache->redirect, cast_uchar "/"); c->cache->incomplete = 0; } else { unsigned char *ls, *le, *le2; unsigned char *ud; unsigned char *t = init_str(); int l = 0; int type = 0; int pos = 0; int a; add_to_str(&t, &l, cast_uchar ""); ud = stracpy(c->url); if (strchr(cast_const_char ud, POST_CHAR)) *cast_uchar strchr(cast_const_char ud, POST_CHAR) = 0; add_conv_str(&t, &l, ud, (int)strlen(cast_const_char ud), -1); mem_free(ud); add_to_str(&t, &l, cast_uchar "
");
			if (si->list == 1 && si->client == SMBC) {
/* smbc has a nasty bug that it writes field descriptions to stderr and data to
   stdout. Because of stdout buffer, they'll get mixed in the output. Try to
   demix them. */
#define SERVER	"Server              Comment\n------              -------\n"
#define WORKGR	"Workgroup           Master\n---------           ------\n"
				unsigned char *spos = cast_uchar strstr(cast_const_char si->text, SERVER);
				unsigned char *gpos;
				unsigned char *p, *pp, *ppp;
				if (spos) memmove(spos, spos + strlen(SERVER), strlen(cast_const_char spos) - strlen(SERVER) + 1);
				gpos = cast_uchar strstr(cast_const_char si->text, WORKGR);
				if (gpos) memmove(gpos, gpos + strlen(WORKGR), strlen(cast_const_char gpos) - strlen(WORKGR) + 1);
				if (!spos && !gpos) goto sc;
				pp = NULL, ppp = NULL, p = si->text;
				while ((p = cast_uchar strstr(cast_const_char p, "\n\n"))) ppp = pp, pp = p + 2, p++;
				if (!pp) goto sc;
				if (!spos || !gpos) ppp = NULL;
				if (spos) {
					if (!ppp) ppp = pp, pp = NULL;
					memmove(ppp + strlen(SERVER), ppp, strlen(cast_const_char ppp) + 1);
					memcpy(ppp, SERVER, strlen(SERVER));
					if (pp) pp += strlen(SERVER);
				}
				if (gpos && pp) {
					memmove(pp + strlen(WORKGR), pp, strlen(cast_const_char pp) + 1);
					memcpy(pp, WORKGR, strlen(WORKGR));
				}
				goto sc;
			}
			sc:
			ls = si->text;
			while ((le = cast_uchar strchr(cast_const_char ls, '\n'))) {
				unsigned char *lx;
				unsigned char *st;
				le2 = cast_uchar strchr(cast_const_char ls, '\r');
				if (!le2 || le2 > le) le2 = le;
				lx = memacpy(ls, le2 - ls);
				if (si->list == 1) {
					unsigned char *ll, *lll;
					if (!*lx) type = 0;
					if (strstr(cast_const_char lx, "Sharename") && (st = cast_uchar strstr(cast_const_char lx, "Type"))) {
						pos = (int)(st - lx);
						type = 1;
						goto af;
					}
					if (strstr(cast_const_char lx, "Server") && strstr(cast_const_char lx, "Comment")) {
						type = 2;
						goto af;
					}
					if (strstr(cast_const_char lx, "Workgroup") && (st = cast_uchar strstr(cast_const_char lx, "Master"))) {
						pos = (int)(st - lx);
						type = 3;
						goto af;
					}
					if (!type) goto af;
					for (ll = lx; *ll; ll++) if (!WHITECHAR(*ll) && *ll != '-') goto np;
					goto af;
					np:
					for (ll = lx; *ll; ll++) if (!WHITECHAR(*ll)) break;
					for (lll = ll; *lll /* && lll[1]*/; lll++) if (WHITECHAR(*lll) /*&& WHITECHAR(lll[1])*/) break;
					if (type == 1) {
						unsigned char *llll;
						if (!strstr(cast_const_char lll, "Disk")) goto af;
						if (pos && (size_t)pos < strlen(cast_const_char lx) && WHITECHAR(*(llll = lx + pos - 1)) && llll > ll) {
							while (llll > ll && WHITECHAR(*llll)) llll--;
							if (!WHITECHAR(*llll)) lll = llll + 1;
						}
						add_conv_str(&t, &l, lx, (int)(ll - lx), 0);
						add_to_str(&t, &l, cast_uchar "");
						add_conv_str(&t, &l, ll, (int)(lll - ll), 0);
						add_to_str(&t, &l, cast_uchar "");
						add_conv_str(&t, &l, lll, (int)strlen(cast_const_char lll), 0);
					} else if (type == 2) {
						sss:
						add_conv_str(&t, &l, lx, (int)(ll - lx), 0);
						add_to_str(&t, &l, cast_uchar "");
						add_conv_str(&t, &l, ll, (int)(lll - ll), 0);
						add_to_str(&t, &l, cast_uchar "");
						add_conv_str(&t, &l, lll, (int)strlen(cast_const_char lll), 0);
					} else if (type == 3) {
						if ((size_t)pos < strlen(cast_const_char lx) && pos && WHITECHAR(lx[pos - 1]) && !WHITECHAR(lx[pos])) ll = lx + pos;
						else for (ll = lll; *ll; ll++) if (!WHITECHAR(*ll)) break;
						for (lll = ll; *lll; lll++) if (WHITECHAR(*lll)) break;
						goto sss;
					} else goto af;
				} else if (si->list == 2 && si->client == SMBCLIENT) {
					if (strstr(cast_const_char lx, "NT_STATUS")) {
						le[1] = 0;
						goto af;
					}
					if (le2 - ls >= 5 && ls[0] == ' ' && ls[1] == ' ' && ls[2] != ' ') {
						int dir;
						unsigned char *pp;
						unsigned char *p = ls + 3;
						while (le2 - p >= 2) {
							if (p[0] == ' ' && p[1] == ' ') goto o;
							p++;
						}
						goto af;
						o:
						dir = 0;
						pp = p;
						while (pp < le2 && *pp == ' ') pp++;
						while (pp < le2 && *pp != ' ') {
							if (*pp == 'D') {
								dir = 1;
								break;
							}
							pp++;
						}
						add_to_str(&t, &l, cast_uchar "  ");
						add_conv_str(&t, &l, ls + 2, (int)(p - (ls + 2)), 0);
						add_to_str(&t, &l, cast_uchar "");
						add_conv_str(&t, &l, p, (int)(le - p), 0);
					} else goto af;
				} else if (si->list == 2 && si->client == SMBC) {
					unsigned char *d;
					if (le2 - ls <= 17) goto af;
					d = ls + 17;
					smbc_next_chr:
					if (d + 9 >= le2) goto af;
					if (!(d[0] == ':' && d[1] >= '0' && d[1] <= '9' && d[2] >= '0' && d[2] <= '9' && d[3] == ' ' && ((d[4] == '1' && d[5] == '9') || (d[4] == '2' && d[5] >= '0' && d[5] <= '9')) && d[6] >= '0' && d[6] <= '9' && d[7] >= '0' && d[7] <= '9' && d[8] == ' ')) {
						d++;
						goto smbc_next_chr;
					}
					d += 9;
					add_conv_str(&t, &l, ls, (int)(d - ls), 0);
					add_to_str(&t, &l, cast_uchar "");
					add_conv_str(&t, &l, d, (int)(le2 - d), 0);
					add_to_str(&t, &l, cast_uchar "");
				} else af: add_conv_str(&t, &l, ls, (int)(le2 - ls), 0);
				add_chr_to_str(&t, &l, '\n');
				ls = le + 1;
				mem_free(lx);
			}
			/*add_to_str(&t, &l, si->text);*/
			a = add_fragment(c->cache, 0, t, l);
			if (a < 0) {
				mem_free(t);
				setcstate(c, a);
				abort_connection(c);
				return;
			}
			c->from += l;
			truncate_entry(c->cache, l, 1);
			c->cache->incomplete = 0;
			mem_free(t);
			if (!c->cache->head) c->cache->head = stracpy(cast_uchar "\r\n");
			add_to_strn(&c->cache->head, cast_uchar "Content-Type: text/html\r\n");
		}
	} else {
		truncate_entry(c->cache, c->from, 1);
		c->cache->incomplete = 0;
	}
	close_socket(&c->sock1);
	close_socket(&c->sock2);
	setcstate(c, S__OK);
	abort_connection(c);
	return;
}

#endif