#include "compat.h"
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_GETPEERNAME
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#endif
#include "net.h"
#include "opennet.h"
#ifndef MSG_WAITALL
#define MSG_WAITALL 0
#endif
/*
* Legacy stub, please ignore.
*/
int opennet_init(void) {
return(0);
}
/*
Replace
@@OSNM@@ OS Name (linux, freebsd, sunos, etc)
@@OSVR@@ OS version (2.2.x, 4.2, 5.8, etc)
@@OSVS@@ OS version (short) (2.2, 4.2, 5.8, etc)
@@ARCH@@ Arch (i386, sparc64, sun4u, sun4m, etc)
*/
char *parse_url_subst(const char *src) {
char *retval = NULL;
if (!strstr(src, "@@")) {
return(strdup(src));
}
return(retval);
}
/*
* Parse a URL into its components
*/
static int parse_url(char *url, char **scheme, char **user, char **pass, char **host, int *port, char **path) {
char *tmptr, *schemeptr, *pathptr, *userptr, *passptr, *hostptr, *portptr;
if (!url) {
return(-1);
}
tmptr = strstr(url, "://");
if (!tmptr) {
return(-1);
}
if (strlen(tmptr) <= 3) {
return(-1);
}
*tmptr = '\0';
schemeptr = url;
tmptr += 3;
pathptr = strchr(tmptr, '/');
if (pathptr) {
*pathptr = '\0';
pathptr++;
} else {
pathptr = "";
}
hostptr = strchr(tmptr, '@');
if (hostptr) {
*hostptr = '\0';
hostptr++;
userptr = tmptr;
passptr = strchr(userptr, ':');
if (passptr) {
*passptr = '\0';
passptr++;
}
} else {
userptr = NULL;
passptr = NULL;
hostptr = tmptr;
}
portptr = strchr(hostptr, ':');
if (portptr) {
*portptr = '\0';
portptr++;
*port = strtoul(portptr, NULL, 10);
} else {
if (strcasecmp(schemeptr, "http") == 0) {
*port = 80;
} else if (strcasecmp(schemeptr, "ftp") == 0) {
*port = 21;
} else {
*port = 0;
}
}
*scheme = schemeptr;
*user = userptr;
*pass = passptr;
*host = hostptr;
*path = pathptr;
return(0);
}
static ssize_t read_net_internal(int fd, void *buf, size_t count, int socketStatus) {
ssize_t retval = 0;
ssize_t read_ret;
int socket = 0;
#ifdef HAVE_GETPEERNAME
int gpn_ret;
struct sockaddr_in tmp;
int tmplen;
if (socketStatus < 0) {
tmplen = sizeof(tmp);
gpn_ret = getpeername(fd, (struct sockaddr *) &tmp, &tmplen);
if (gpn_ret >= 0) {
socket = 1;
} else {
socket = 0;
}
} else {
socket = socketStatus;
}
#else
socket = socketStatus;
#endif
while (count) {
if (socket) {
read_ret = recv(fd, buf, count, 0);
} else {
read_ret = read(fd, buf, count);
}
if (read_ret == 0) {
break;
}
if (read_ret < 0) {
retval = read_ret;
break;
}
count -= read_ret;
buf += read_ret;
retval += read_ret;
}
return(retval);
}
static int open_net_http(char *user, char *pass, char *host, int port, char *path, int flags, off_t offset, off_t *length) {
unsigned long long content_length = 0;
ssize_t read_ret, sink_bytes = 0;
ssize_t send_ret, recv_ret;
char httpcmd[4096], charbuf, httpreply[1024], *http_response;
char garbage[4096];
int bytes_to_read;
int fd;
int snprintf_ret;
int http_response_code = 0;
int newlinecount = 0, linenum = 0, i = 0;
fd = net_connect_tcp(host, port);
if (fd < 0) {
return(-1);
}
if (offset == 0) {
snprintf_ret = snprintf(httpcmd, sizeof(httpcmd), "GET /%s HTTP/1.0\015\012Host: %s\015\012\015\012", path, host);
} else {
snprintf_ret = snprintf(httpcmd, sizeof(httpcmd), "GET /%s HTTP/1.0\015\012Host: %s\015\012Range: bytes=%llu-\015\012\015\012", path, host, (unsigned long long) offset);
}
if (snprintf_ret >= sizeof(httpcmd)) {
return(-1);
}
send_ret = send(fd, httpcmd, snprintf_ret, 0);
if (send_ret != snprintf_ret) {
net_close(fd);
return(-1);
}
while (1) {
recv_ret = recv(fd, &charbuf, 1, MSG_WAITALL);
if (charbuf == '\015' || charbuf == '\012') {
if (i > 0) {
httpreply[i] = '\0';
if (linenum == 0) {
if (strlen(httpreply) < 9) {
net_close(fd);
return(-1);
}
http_response = httpreply + 9;
http_response_code = strtoul(http_response, NULL, 10);
switch (http_response_code) {
case 206:
if (offset == 0) {
CHECKPOINT;
net_close(fd);
return(-1);
}
break;
case 200:
if (offset != 0) {
sink_bytes = offset;
}
break;
case 301:
case 302:
break;
case 404:
CHECKPOINT;
default:
net_close(fd);
return(-1);
}
} else {
if (strncasecmp(httpreply, "Content-Length:", 15) == 0) {
content_length = strtoull(httpreply + 16, NULL, 10);
if (offset != 0 && http_response_code == 206) {
content_length += (unsigned long long) offset;
}
if (length) {
*length = content_length;
}
}
if (http_response_code == 301 || http_response_code == 302) {
if (strncasecmp(httpreply, "Location:", 9) == 0) {
net_close(fd);
return(open_net(httpreply + 10, flags, 0));
}
}
}
}
newlinecount++;
linenum++;
i = 0;
} else {
newlinecount = 0;
httpreply[i++] = charbuf;
}
if (newlinecount == 4) {
break;
}
if (recv_ret <= 0) {
net_close(fd);
return(-1);
}
}
if (sink_bytes) {
while (sink_bytes) {
bytes_to_read = sink_bytes;
if (bytes_to_read > sizeof(garbage)) {
bytes_to_read = sizeof(garbage);
}
read_ret = read_net_internal(fd, garbage, bytes_to_read, 1);
if (read_ret <= 0) {
SPOTVAR_I(read_ret);
net_close(fd);
return(-1);
}
sink_bytes -= read_ret;
}
}
return(fd);
}
static int open_net_ftp(char *user, char *pass, char *host, int port, char *path, int flags, off_t offset, off_t *length) {
return(-1);
}
static int open_net_internal(const char *pathname, int flags, mode_t mode, int *socket, off_t offset, off_t *length) {
char *pathname_dup;
char *scheme, *user, *pass, *host, *path;
off_t lseek_ret;
int port = 0;
int parse_ret;
int retval = -1;
if (socket) {
*socket = 0;
}
if (!pathname) {
retval = open(pathname, flags, mode);
if (offset && retval >= 0) {
lseek_ret = lseek(retval, offset, SEEK_SET);
if (lseek_ret != offset) {
close(retval);
return(-1);
}
}
return(retval);
}
pathname_dup = strdup(pathname);
if (!pathname_dup) {
retval = open(pathname, flags, mode);
if (offset && retval >= 0) {
lseek_ret = lseek(retval, offset, SEEK_SET);
if (lseek_ret != offset) {
close(retval);
return(-1);
}
}
return(retval);
}
parse_ret = parse_url(pathname_dup, &scheme, &user, &pass, &host, &port, &path);
if (parse_ret < 0 || port == 0 || !host || !scheme) {
free(pathname_dup);
retval = open(pathname, flags, mode);
if (offset && retval >= 0) {
lseek_ret = lseek(retval, offset, SEEK_SET);
if (lseek_ret != offset) {
close(retval);
return(-1);
}
}
return(retval);
}
if (length) {
*length = -1;
}
if (strcasecmp(scheme, "http") == 0) {
retval = open_net_http(user, pass, host, port, path, flags, offset, length);
}
if (strcasecmp(scheme, "ftp") == 0) {
retval = open_net_ftp(user, pass, host, port, path, flags, offset, length);
}
free(pathname_dup);
if (retval < 0) {
retval = open(pathname, flags, mode);
if (offset && retval >= 0) {
lseek_ret = lseek(retval, offset, SEEK_SET);
if (lseek_ret != offset) {
close(retval);
return(-1);
}
}
} else {
if (socket) {
*socket = 1;
}
}
return(retval);
}
NETFILE *fopen_net(const char *pathname, const char *mode) {
NETFILE *ret;
int is_sock;
off_t length;
ret = malloc(sizeof(*ret));
if (!ret) {
return(NULL);
}
ret->fd = open_net_internal(pathname, O_RDONLY, 0666, &is_sock, 0, &length);
if (ret->fd < 0) {
free(ret);
return(NULL);
}
ret->free_buf = 1;
ret->bufsize_s = ret->bufsize = 32768;
ret->buf_s = ret->buf = malloc(ret->bufsize);
ret->bufused = 0;
ret->eof = 0;
ret->pos = 0;
ret->socket = is_sock;
ret->length = length;
ret->url = strdup(pathname);
return(ret);
}
int feof_net(NETFILE *stream) {
if (!stream) {
return(-1);
}
return(stream->eof);
}
size_t fread_net(void *ptr, size_t size, size_t nmemb, NETFILE *stream) {
ssize_t recv_ret;
size_t retval;
int bytes_to_copy;
if (!stream) {
return(0);
}
if (stream->fd >= 0) {
while (stream->bufused < (size * nmemb)) {
if (stream->socket) {
recv_ret = recv(stream->fd, stream->buf + stream->bufused, stream->bufsize - stream->bufused, MSG_WAITALL);
} else {
recv_ret = read(stream->fd, stream->buf + stream->bufused, stream->bufsize - stream->bufused);
}
if (recv_ret <= 0) {
net_close(stream->fd);
stream->fd = -1;
break;
}
stream->bufused += recv_ret;
if (stream->bufused >= (size * nmemb)) {
break;
}
/*
* If there is more data to be read, but the buffer is full
* try to make room, or quit trying to read data
*/
if (stream->bufused == stream->bufsize) {
if (stream->bufsize_s != stream->bufsize) {
CHECKPOINT;
memmove(stream->buf_s, stream->buf, stream->bufused);
stream->buf = stream->buf_s;
stream->bufsize = stream->bufsize_s;
} else {
break;
}
}
}
}
if (stream->bufused == 0) {
if (stream->fd < 0) {
stream->eof = 1;
}
return(0);
}
bytes_to_copy = (size * nmemb);
if (stream->bufused < bytes_to_copy) {
bytes_to_copy = stream->bufused;
/* Adjust bytes_to_copy to be a multiple of size */
bytes_to_copy /= size;
bytes_to_copy *= size;
}
memcpy(ptr, stream->buf, bytes_to_copy);
stream->buf += bytes_to_copy;
stream->bufused -= bytes_to_copy;
stream->bufsize -= bytes_to_copy;
if (stream->bufused == 0) {
stream->buf = stream->buf_s;
stream->bufsize = stream->bufsize_s;
}
retval = bytes_to_copy / size;
stream->pos += retval;
return(retval);
}
char *fgets_net(char *s, int size, NETFILE *stream) {
ssize_t recv_ret;
int bytes_to_copy;
char *stopptr;
if (!stream) {
return(NULL);
}
if (stream->fd >= 0) {
while (stream->bufused < size) {
if (stream->socket) {
recv_ret = recv(stream->fd, stream->buf + stream->bufused, stream->bufsize - stream->bufused, MSG_WAITALL);
} else {
recv_ret = read(stream->fd, stream->buf + stream->bufused, stream->bufsize - stream->bufused);
}
if (recv_ret <= 0) {
net_close(stream->fd);
stream->fd = -1;
break;
}
stream->bufused += recv_ret;
if (memchr(stream->buf, '\n', stream->bufused)) {
break;
}
}
}
if (stream->bufused == 0) {
if (stream->fd < 0) {
stream->eof = 1;
}
return(NULL);
}
stopptr = memchr(stream->buf, '\n', stream->bufused);
if (!stopptr) {
stopptr = stream->buf + stream->bufused;
} else {
stopptr++;
}
bytes_to_copy = stopptr - stream->buf;
if (size < bytes_to_copy) {
bytes_to_copy = size;
}
memcpy(s, stream->buf, bytes_to_copy);
if (bytes_to_copy < size) {
s[bytes_to_copy] = '\0';
} else {
s[size - 1] = '\0';
}
stream->buf += bytes_to_copy;
stream->bufused -= bytes_to_copy;
stream->bufsize -= bytes_to_copy;
stream->pos += bytes_to_copy;
if (stream->bufused == 0) {
stream->buf = stream->buf_s;
stream->bufsize = stream->bufsize_s;
}
return(s);
}
int fclose_net(NETFILE *stream) {
int fd;
int retval;
int is_sock;
if (!stream) {
return(-1);
}
fd = stream->fd;
is_sock = stream->socket;
if (stream->buf_s && stream->free_buf) {
free(stream->buf_s);
}
if (stream->url) {
free(stream->url);
}
free(stream);
if (is_sock) {
retval = net_close(fd);
} else {
retval = close(fd);
}
return(retval);
}
int setvbuf_net(NETFILE *stream, char *buf, int mode, size_t size) {
if (!stream) {
return(-1);
}
if (size < stream->bufused) {
return(-1);
}
if (stream->buf_s && stream->free_buf) {
free(stream->buf_s);
}
memcpy(buf, stream->buf, stream->bufused);
stream->buf = buf;
stream->buf_s = buf;
stream->bufsize = size;
stream->bufsize_s = size;
stream->free_buf = 0;
return(0);
}
int open_net(const char *pathname, int flags, mode_t mode) {
int tmp;
return(open_net_internal(pathname, flags, mode, &tmp, 0, NULL));
}
ssize_t read_net(int fd, void *buf, size_t count) {
return(read_net_internal(fd, buf, count, -1));
}
off_t lseek_net(int filedes, off_t offset, int whence) {
return(-1);
}
int fseeko_net(NETFILE *stream, off_t offset, int whence) {
static char garbage[4096];
size_t fread_ret, bytes_to_read;
off_t newoffset, lseek_ret;
off_t newlength;
int newfd;
if (!stream) {
return(-1);
}
if (stream->fd < 0) {
return(-1);
}
switch (whence) {
case SEEK_CUR:
newoffset = stream->pos + offset;
break;
case SEEK_END:
newoffset = stream->length + offset;
break;
case SEEK_SET:
newoffset = offset;
break;
default:
CHECKPOINT;
return(-1);
break;
}
if (newoffset < 0) {
return(-1);
}
lseek_ret = lseek(stream->fd, newoffset, SEEK_SET);
if (lseek_ret != ((off_t) -1)) {
stream->pos = lseek_ret;
stream->buf = stream->buf_s;
stream->bufused = 0;
stream->bufsize = stream->bufsize_s;
return(0);
}
/*
* Read up to 4K of data to avoid opening a new connection
*/
if (newoffset >= stream->pos && (newoffset - stream->pos < sizeof(garbage))) {
bytes_to_read = newoffset - stream->pos;
fread_ret = fread_net(garbage, sizeof(garbage[0]), bytes_to_read, stream);
if (fread_ret == bytes_to_read) {
return(0);
}
}
newfd = open_net_internal(stream->url, O_RDONLY, 0666, NULL, newoffset, &newlength);
if (newfd < 0) {
return(-1);
}
net_close(stream->fd);
stream->fd = newfd;
stream->pos = newoffset;
stream->buf = stream->buf_s;
stream->bufused = 0;
stream->bufsize = stream->bufsize_s;
stream->eof = 0;
return(0);
}
int fseek_net(NETFILE *stream, long offset, int whence) {
return(fseek_net(stream, offset, whence));
}
off_t ftello_net(NETFILE *stream) {
if (!stream) {
return(-1);
}
if (stream->fd < 0) {
return(-1);
}
return(stream->pos);
}
long ftell_net(NETFILE *stream) {
return(ftello_net(stream));
}
off_t flength_net(NETFILE *stream) {
if (!stream) {
return(-1);
}
if (stream->fd < 0) {
return(-1);
}
return(stream->length);
}
syntax highlighted by Code2HTML, v. 0.9.1