1 /* Internal FSP protocol implementation */
4 #define _GNU_SOURCE /* Needed for asprintf() */
17 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
25 #include "cache/cache.h"
26 #include "config/options.h"
27 #include "intl/gettext/libintl.h"
28 #include "main/module.h"
29 #include "main/select.h"
30 #include "network/connection.h"
31 #include "network/socket.h"
32 #include "osdep/osdep.h"
33 #include "protocol/auth/auth.h"
34 #include "protocol/common.h"
35 #include "protocol/protocol.h"
36 #include "protocol/fsp/fsp.h"
37 #include "protocol/uri.h"
38 #include "util/conv.h"
39 #include "util/memory.h"
40 #include "util/snprintf.h"
41 #include "util/string.h"
43 struct module fsp_protocol_module
= struct_module(
44 /* name: */ N_("FSP"),
47 /* submodules: */ NULL
,
54 /* Because functions of fsplib block waiting for a response from the
55 * server, and ELinks wants non-blocking operations so that other
56 * connections and the user interface keep working, this FSP protocol
57 * module forks a child process for each FSP connection. The child
58 * process writes the results to two pipes, which the main ELinks
59 * process then reads in a non-blocking fashion. The child process
60 * gets these pipes as its stdout and stderr.
62 * - If an error occurs, the child process writes "text/x-error"
63 * without newline to stderr, and an error code and a newline to
64 * stdout. The error code is either from errno or a negated value
65 * from enum connection_state, e.g. -S_OUT_OF_MEM. In particular,
66 * EPERM causes the parent process to prompt for username and
67 * password. (In this, fsplib differs from libsmbclient, which uses
68 * EACCES if authentication fails.)
70 * - If the resource is a regular file, the child process writes the
71 * estimated length of the file (in bytes) and a newline to stderr,
72 * and the contents of the file to stdout.
74 * - If the resource is a directory, the child process writes
75 * "text/html" without newline to stderr, and an HTML rendering
76 * of the directory listing to stdout.
78 * The exit code of the child process also indicates whether an error
79 * occurred, but the parent process ignores it. */
81 /* FSP synchronous connection management (child process):
83 * The child process generally does not bother to free the memory it
84 * allocates. When the process exits, the operating system will free
85 * the memory anyway. There is no point in changing this, because the
86 * child process also inherits memory allocations from the parent
87 * process, and it would be very cumbersome to free those. */
89 /* FIXME: Although it is probably not so much an issue, check if writes to
90 * stdout fails for directory listing like we do for file fetching. */
95 printf("%d\n", error
);
96 fprintf(stderr
, "text/x-error");
97 /* In principle, this should perhaps call fsp_close_session to
98 * make the server accept any key from the next client process
99 * at this IP address. That doesn't seem necessary though:
100 * fsplib uses various IPC schemes to synchronize the use of
101 * server-provided keys between client processes, so the next
102 * client process will probably be able to use the key saved
108 compare(const void *av
, const void *bv
)
110 const FSP_RDENTRY
*a
= av
, *b
= bv
;
111 int res
= ((b
->type
== FSP_RDTYPE_DIR
) - (a
->type
== FSP_RDTYPE_DIR
));
115 return strcmp(a
->name
, b
->name
);
119 display_entry(const FSP_RDENTRY
*fentry
, const unsigned char dircolor
[])
121 struct string string
;
123 /* fentry->name is a fixed-size array and is followed by other
124 * members; thus, if the name reported by the server does not
125 * fit in the array, fsplib must either truncate or reject it.
126 * If fsplib truncates the name, it does not document whether
127 * fentry->namlen is the original length or the truncated
128 * length. ELinks therefore ignores fentry->namlen and
129 * instead measures the length on its own. */
130 const size_t namelen
= strlen(fentry
->name
);
132 if (!init_string(&string
)) return;
133 add_format_to_string(&string
, "%10d", fentry
->size
);
134 add_to_string(&string
, "\t<a href=\"");
135 /* The result of encode_uri_string does not include '&' or '<'
136 * which could mess up the HTML. */
137 encode_uri_string(&string
, fentry
->name
, namelen
, 0);
138 if (fentry
->type
== FSP_RDTYPE_DIR
) {
139 add_to_string(&string
, "/\">");
141 add_to_string(&string
, "<font color=\"");
142 add_to_string(&string
, dircolor
);
143 add_to_string(&string
, "\"><b>");
145 add_html_to_string(&string
, fentry
->name
, namelen
);
147 add_to_string(&string
, "</b></font>");
150 add_to_string(&string
, "\">");
151 add_html_to_string(&string
, fentry
->name
, namelen
);
153 add_to_string(&string
, "</a>");
155 done_string(&string
);
159 sort_and_display_entries(FSP_DIR
*dir
, const unsigned char dircolor
[])
161 /* fsp_readdir_native in fsplib 0.9 and earlier requires
162 * the third parameter to point to a non-null pointer
163 * even though it does not dereference that pointer
164 * and overwrites it with another one anyway.
165 * http://sourceforge.net/tracker/index.php?func=detail&aid=1875210&group_id=93841&atid=605738
166 * Work around the bug by using non-null &tmp.
167 * Nothing will actually read or write tmp. */
168 FSP_RDENTRY fentry
, tmp
, *table
= NULL
;
169 FSP_RDENTRY
*fresult
= &tmp
;
173 while (!fsp_readdir_native(dir
, &fentry
, &fresult
)) {
174 FSP_RDENTRY
*new_table
;
177 if (!strcmp(fentry
.name
, "."))
179 new_table
= mem_realloc(table
, (size
+ 1) * sizeof(*table
));
183 copy_struct(&table
[size
], &fentry
);
186 /* If size==0, then table==NULL. According to ISO/IEC 9899:1999
187 * 7.20.5p1, the NULL must not be given to qsort. */
189 qsort(table
, size
, sizeof(*table
), compare
);
191 for (i
= 0; i
< size
; i
++) {
192 display_entry(&table
[i
], dircolor
);
197 fsp_directory(FSP_SESSION
*ses
, struct uri
*uri
)
201 unsigned char *data
= get_uri_string(uri
, URI_DATA
);
202 unsigned char dircolor
[8] = "";
205 fsp_error(-S_OUT_OF_MEM
);
207 if (init_directory_listing(&buf
, uri
) != S_OK
)
208 fsp_error(-S_OUT_OF_MEM
);
210 dir
= fsp_opendir(ses
, data
);
211 if (!dir
) fsp_error(errno
);
213 fprintf(stderr
, "text/html");
218 if (get_opt_bool("document.browse.links.color_dirs", NULL
)) {
219 color_to_string(get_opt_color("document.colors.dirs", NULL
),
223 sort_and_display_entries(dir
, dircolor
);
225 puts("</pre><hr/></body></html>");
226 fsp_close_session(ses
);
230 #define READ_SIZE 4096
233 do_fsp(struct connection
*conn
)
237 struct uri
*uri
= conn
->uri
;
238 struct auth_entry
*auth
;
239 unsigned char *host
= get_uri_string(uri
, URI_HOST
);
240 unsigned char *data
= get_uri_string(uri
, URI_DATA
);
241 unsigned short port
= (unsigned short)get_uri_port(uri
);
242 unsigned char *password
= NULL
;
245 if (uri
->passwordlen
) {
246 password
= get_uri_string(uri
, URI_PASSWORD
);
248 auth
= find_auth(uri
);
249 if (auth
) password
= auth
->password
;
252 ses
= fsp_open_session(host
, port
, password
);
253 if (!ses
) fsp_error(errno
);
255 /* fsplib 0.8 ABI depends on _FILE_OFFSET_BITS
256 * https://sourceforge.net/tracker/index.php?func=detail&aid=1674729&group_id=93841&atid=605738
257 * If ELinks and fsplib are using different values of
258 * _FILE_OFFSET_BITS, then they get different definitions of
259 * struct stat, and the st_size stored by fsp_stat is
260 * typically not the same as the st_size read by ELinks.
261 * Fortunately, st_mode seems to have the same offset and size
262 * in both versions of struct stat.
264 * If all the bytes used by the 32-bit st_size are also used
265 * by the 64-bit st_size, then ELinks may be able to guess
266 * which ones they are, because the current version 2 of FSP
267 * supports only 32-bit file sizes in protocol packets. Begin
268 * by filling struct stat with 0xAA so that it's easier to
269 * detect which bytes fsp_stat has left unchanged. (Only
270 * sb.st_size really needs to be filled, but filling the rest
271 * too helps viewing the data with a debugger.) */
272 memset(&sb
, 0xAA, sizeof(sb
));
273 if (fsp_stat(ses
, data
, &sb
)) fsp_error(errno
);
275 if (S_ISDIR(sb
.st_mode
)) {
276 fsp_directory(ses
, uri
);
277 } else { /* regular file */
279 FSP_FILE
*file
= fsp_fopen(ses
, data
, "r");
286 #if SIZEOF_OFF_T >= 8
287 if (sb
.st_size
< 0 || sb
.st_size
> 0xFFFFFFFF) {
288 /* Probably a _FILE_OFFSET_BITS mismatch as
289 * described above. Try to detect which half
290 * of st_size is the real size. This may
291 * depend on the endianness of the processor
292 * and on the padding in struct stat. */
293 if ((sb
.st_size
& 0xFFFFFFFF00000000ULL
) == 0xAAAAAAAA00000000ULL
)
294 sb
.st_size
= sb
.st_size
& 0xFFFFFFFF;
295 else if ((sb
.st_size
& 0xFFFFFFFF) == 0xAAAAAAAA)
296 sb
.st_size
= (sb
.st_size
>> 32) & 0xFFFFFFFF;
297 else /* Can't figure it out. */
303 fprintf(stderr
, "%" OFF_T_FORMAT
"\n", (off_t
)(sb
.st_size
));
306 while ((r
= fsp_fread(buf
, 1, READ_SIZE
, file
)) > 0) {
310 int w
= safe_write(STDOUT_FILENO
, buf
+ off
, r
);
312 if (w
== -1) goto out
;
319 fsp_close_session(ses
);
328 /* FSP asynchronous connection management (parent process): */
330 /* Kill the current connection and ask for a username/password for the next
333 prompt_username_pw(struct connection
*conn
)
335 add_auth_entry(conn
->uri
, "FSP", NULL
, NULL
, 0);
336 abort_connection(conn
, S_OK
);
340 fsp_got_error(struct socket
*socket
, struct read_buffer
*rb
)
342 int len
= rb
->length
;
343 struct connection
*conn
= socket
->conn
;
347 abort_connection(conn
, -errno
);
351 /* There should be free space in the buffer, because
352 * @alloc_read_buffer allocated several kibibytes, and the
353 * child process wrote only an integer and a newline to the
355 assert(rb
->freespace
>= 1);
357 abort_connection(conn
, S_INTERNAL
);
360 rb
->data
[len
] = '\0';
361 error
= atoi(rb
->data
);
362 kill_buffer_data(rb
, len
);
365 prompt_username_pw(conn
);
368 abort_connection(conn
, -error
);
374 fsp_got_data(struct socket
*socket
, struct read_buffer
*rb
)
376 int len
= rb
->length
;
377 struct connection
*conn
= socket
->conn
;
380 abort_connection(conn
, -errno
);
385 abort_connection(conn
, S_OK
);
389 socket
->state
= SOCKET_END_ONCLOSE
;
390 conn
->received
+= len
;
391 if (add_fragment(conn
->cached
, conn
->from
, rb
->data
, len
) == 1)
394 kill_buffer_data(rb
, len
);
396 read_from_socket(socket
, rb
, S_TRANS
, fsp_got_data
);
400 fsp_got_header(struct socket
*socket
, struct read_buffer
*rb
)
402 struct connection
*conn
= socket
->conn
;
403 struct read_buffer
*buf
;
406 conn
->cached
= get_cache_entry(conn
->uri
);
408 /* Even though these are pipes rather than real
409 * sockets, call close_socket instead of close, to
410 * ensure that abort_connection won't try to close the
411 * file descriptors again. (Could we skip the calls
412 * and assume abort_connection will do them?) */
413 close_socket(socket
);
414 close_socket(conn
->data_socket
);
415 abort_connection(conn
, S_OUT_OF_MEM
);
418 socket
->state
= SOCKET_END_ONCLOSE
;
420 if (rb
->length
> 0) {
421 unsigned char *ctype
= memacpy(rb
->data
, rb
->length
);
423 if (ctype
&& *ctype
) {
424 if (!strcmp(ctype
, "text/x-error")) {
428 if (ctype
[0] >= '0' && ctype
[0] <= '9') {
430 conn
->est_length
= (off_t
)atoll(ctype
);
432 conn
->est_length
= (off_t
)atol(ctype
);
436 /* avoid read from socket error */
437 if (!conn
->est_length
) {
438 abort_connection(conn
, S_OK
);
442 else mem_free_set(&conn
->cached
->content_type
, ctype
);
449 buf
= alloc_read_buffer(conn
->data_socket
);
451 close_socket(socket
);
452 close_socket(conn
->data_socket
);
453 abort_connection(conn
, S_OUT_OF_MEM
);
458 mem_free_set(&conn
->cached
->content_type
, stracpy("text/html"));
459 read_from_socket(conn
->data_socket
, buf
, S_CONN
, fsp_got_error
);
461 read_from_socket(conn
->data_socket
, buf
, S_CONN
, fsp_got_data
);
467 fsp_protocol_handler(struct connection
*conn
)
469 int fsp_pipe
[2] = { -1, -1 };
470 int header_pipe
[2] = { -1, -1 };
473 if (c_pipe(fsp_pipe
) || c_pipe(header_pipe
)) {
476 if (fsp_pipe
[0] >= 0) close(fsp_pipe
[0]);
477 if (fsp_pipe
[1] >= 0) close(fsp_pipe
[1]);
478 if (header_pipe
[0] >= 0) close(header_pipe
[0]);
479 if (header_pipe
[1] >= 0) close(header_pipe
[1]);
480 abort_connection(conn
, -s_errno
);
484 conn
->unrestartable
= 1;
485 find_auth(conn
->uri
); /* remember username and password */
493 close(header_pipe
[0]);
494 close(header_pipe
[1]);
495 retry_connection(conn
, -s_errno
);
500 dup2(fsp_pipe
[1], 1);
501 dup2(open("/dev/null", O_RDONLY
), 0);
502 dup2(header_pipe
[1], 2);
504 close(header_pipe
[0]);
506 /* There may be outgoing data in stdio buffers
507 * inherited from the parent process. The parent
508 * process is going to write this data, so the child
509 * process must not do that. Closing the file
510 * descriptors ensures this.
512 * FIXME: If something opens more files and gets the
513 * same file descriptors and does not close them
514 * before exit(), then stdio may attempt to write the
515 * buffers to the wrong files. This might happen for
516 * example if fsplib calls syslog(). */
517 close_all_non_term_fd();
521 struct read_buffer
*buf2
;
523 conn
->data_socket
->fd
= fsp_pipe
[0];
524 conn
->socket
->fd
= header_pipe
[0];
525 set_nonblocking_fd(conn
->data_socket
->fd
);
526 set_nonblocking_fd(conn
->socket
->fd
);
528 close(header_pipe
[1]);
529 buf2
= alloc_read_buffer(conn
->socket
);
531 close_socket(conn
->data_socket
);
532 close_socket(conn
->socket
);
533 abort_connection(conn
, S_OUT_OF_MEM
);
536 read_from_socket(conn
->socket
, buf2
, S_CONN
, fsp_got_header
);