📄 smb.c
字号:
/* Internal SMB protocol implementation *//* $Id: smb.c,v 1.66.4.2 2005/05/01 21:15:32 jonas Exp $ */#ifndef _GNU_SOURCE#define _GNU_SOURCE /* Needed for asprintf() */#endif#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <errno.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#ifdef HAVE_SYS_TIME_H#include <sys/time.h> /* FreeBSD needs this before resource.h */#endif#include <sys/types.h> /* FreeBSD needs this before resource.h */#ifdef HAVE_SYS_RESOURCE_H#include <sys/resource.h>#endif#ifdef HAVE_FCNTL_H#include <fcntl.h> /* OS/2 needs this after sys/types.h */#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include "elinks.h"#include "cache/cache.h"#include "config/options.h"#include "intl/gettext/libintl.h"#include "lowlevel/connect.h"#include "lowlevel/select.h"#include "modules/module.h"#include "osdep/osdep.h"#include "protocol/protocol.h"#include "protocol/smb/smb.h"#include "protocol/uri.h"#include "sched/connection.h"#include "util/memory.h"#include "util/snprintf.h"#include "util/string.h"/* XXX: Nice cleanup target --pasky *//* FIXME: we rely on smbclient output which may change in future, * so i think we should use libsmbclient instead (or better in addition) * This stuff is a quick hack, but it works ;). --Zas */enum smb_list_type { SMB_LIST_NONE, SMB_LIST_SHARES, SMB_LIST_DIR,};struct smb_connection_info { enum smb_list_type list_type; /* If this is 1, it means one socket is already off. The second one * should call end_smb_connection() when it goes off as well. */ int closing; int textlen; unsigned char text[1];};static void end_smb_connection(struct connection *conn);struct option_info smb_options[] = { INIT_OPT_TREE("protocol", N_("SMB"), "smb", 0, N_("SAMBA specific options.")), INIT_OPT_STRING("protocol.smb", N_("Credentials"), "credentials", 0, "", N_("Credentials file passed to smbclient via -A option.")), NULL_OPTION_INFO,};struct module smb_protocol_module = struct_module( /* name: */ N_("SMB"), /* options: */ smb_options, /* hooks: */ NULL, /* submodules: */ NULL, /* data: */ NULL, /* init: */ NULL, /* done: */ NULL);/* Return 0 if @conn->cached was set. */static intsmb_get_cache(struct connection *conn){ if (conn->cached) return 0; conn->cached = get_cache_entry(conn->uri); if (conn->cached) return 0; abort_conn_with_state(conn, S_OUT_OF_MEM); return -1;}#define READ_SIZE 4096static intsmb_read_data(struct connection *conn, int sock, unsigned char *dst){ int r; struct smb_connection_info *si = conn->info; r = read(sock, dst, READ_SIZE); if (r == -1) { retry_conn_with_state(conn, -errno); return -1; } if (r == 0) { if (!si->closing) { si->closing = 1; clear_handlers(sock); return 0; } end_smb_connection(conn); return 0; } return r;}static voidsmb_read_text(struct connection *conn, int sock){ int r; struct smb_connection_info *si = conn->info; /* We add 2 here to handle LF and NUL chars that are added in * smb_end_connection(). */ si = mem_realloc(si, sizeof(*si) + si->textlen + READ_SIZE + 2); if (!si) { abort_conn_with_state(conn, S_OUT_OF_MEM); return; } conn->info = si; r = smb_read_data(conn, sock, si->text + si->textlen); if (r <= 0) return; if (!conn->from) set_connection_state(conn, S_GETH); si->textlen += r;}static voidsmb_got_data(struct connection *conn){ struct smb_connection_info *si = conn->info; unsigned char buffer[READ_SIZE]; int r; if (si->list_type != SMB_LIST_NONE) { smb_read_text(conn, conn->data_socket.fd); return; } r = smb_read_data(conn, conn->data_socket.fd, buffer); if (r <= 0) return; set_connection_state(conn, S_TRANS); if (smb_get_cache(conn)) return; conn->received += r; if (add_fragment(conn->cached, conn->from, buffer, r) == 1) conn->tries = 0; conn->from += r;}#undef READ_SIZEstatic voidsmb_got_text(struct connection *conn){ smb_read_text(conn, conn->socket.fd);} /* Search for @str1 followed by @str2 in @line. * It returns NULL if not found, or pointer to start * of @str2 in @line if found. */static unsigned char *find_strs(unsigned char *line, unsigned char *str1, unsigned char *str2){ unsigned char *p = strstr(line, str1); if (!p) return NULL; p = strstr(p + strlen(str1), str2); return p;}static voidparse_smbclient_output(struct uri *uri, struct smb_connection_info *si, struct string *page){ unsigned char *line_start, *line_end; enum { SMB_TYPE_NONE, SMB_TYPE_SHARE, SMB_TYPE_SERVER, SMB_TYPE_WORKGROUP } type = SMB_TYPE_NONE; int pos = 0; int stop; assert(uri && si && page); if_assert_failed return; add_to_string(page, "<html><head><title>/"); add_bytes_to_string(page, uri->data, uri->datalen); add_to_string(page, "</title></head><body><pre>"); line_start = si->text; stop = !si->textlen; /* Nothing to parse. */ while (!stop && (line_end = strchr(line_start, ASCII_LF))) { unsigned char *line = line_start; int line_len; int start_offset = 0; /* Handle \r\n case. Normally, do not occur on *nix. */ if (line_end > line_start && line_end[-1] == ASCII_CR) line_end--, start_offset++; line_len = line_end - line_start; /* Here we modify si->text content, this should not be * a problem as it is only used here. This prevents * allocation of memory for the line. */ *line_end = '\0'; /* And I got bored here with cleaning it up. --pasky */ if (si->list_type == SMB_LIST_SHARES) { unsigned char *ll, *lll, *found; if (!*line) type = SMB_TYPE_NONE; found = find_strs(line, "Sharename", "Type"); if (found) { pos = found - line; type = SMB_TYPE_SHARE; goto print_as_is; } found = find_strs(line, "Server", "Comment"); if (found) { type = SMB_TYPE_SERVER; goto print_as_is; } found = find_strs(line, "Workgroup", "Master"); if (found) { pos = found - line; type = SMB_TYPE_WORKGROUP; goto print_as_is; } if (type == SMB_TYPE_NONE) goto print_as_is; for (ll = line; *ll; ll++) if (!isspace(*ll) && *ll != '-') goto print_next; goto print_as_is;print_next: for (ll = line; *ll; ll++) if (!isspace(*ll)) break; for (lll = ll; *lll; lll++) if (isspace(*lll)) break; switch (type) { case SMB_TYPE_SHARE: { unsigned char *llll; if (!strstr(lll, "Disk")) goto print_as_is; if (pos && pos < line_len && isspace(*(llll = line + pos - 1)) && llll > ll) { while (llll > ll && isspace(*llll)) llll--; if (!isspace(*llll)) lll = llll + 1; } add_bytes_to_string(page, line, ll - line); add_to_string(page, "<a href=\""); add_bytes_to_string(page, ll, lll - ll); add_to_string(page, "/\">"); add_bytes_to_string(page, ll, lll - ll); add_to_string(page, "</a>"); add_to_string(page, lll); break; } case SMB_TYPE_WORKGROUP: if (pos < line_len && pos && isspace(line[pos - 1]) && !isspace(line[pos])) { ll = line + pos; } else { for (ll = lll; *ll; ll++) if (!isspace(*ll)) break; } for (lll = ll; *lll; lll++) if (isspace(*lll)) break; /* Fall-through */ case SMB_TYPE_SERVER: add_bytes_to_string(page, line, ll - line); add_to_string(page, "<a href=\"smb://"); add_bytes_to_string(page, ll, lll - ll); add_to_string(page, "/\">"); add_bytes_to_string(page, ll, lll - ll); add_to_string(page, "</a>"); add_to_string(page, lll); break; case SMB_TYPE_NONE: goto print_as_is; } } else if (si->list_type == SMB_LIST_DIR) { if (strstr(line, "NT_STATUS")) { /* Error, stop after message. */ stop = 1; goto print_as_is; } if (line_end - line_start >= 5 && line_start[0] == ' ' && line_start[1] == ' ' && line_start[2] != ' ') { int dir = 0; int may_be_dir = 0; unsigned char *p = line_end; unsigned char *url = line_start + 2; /* smbclient list parser * The boring thing is that output is * ambiguous in many ways: * filenames with more than one space, * etc... * This bloated code tries to do a not * so bad job. --Zas *//* directory D 0 Fri May 7 11:23:18 2004 *//* filename 2444 Thu Feb 19 15:52:46 2004 */ /* Skip end of line */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -