10 #include <sys/socket.h>
13 static int socket_wait(int fd, int is_read)
15 fd_set fds, *fdr = 0, *fdw = 0;
18 tv.tv_sec = 5; tv.tv_usec = 0; // 5 seconds time out
21 if (is_read) fdr = &fds;
23 ret = select(fd+1, fdr, fdw, 0, &tv);
24 if (ret == -1) perror("select");
28 static int kftp_get_response(knetFile *ftp)
33 if (socket_wait(ftp->ctrl_fd, 1) <= 0) return 0;
34 while (read(ftp->ctrl_fd, &c, 1)) { // FIXME: this is *VERY BAD* for unbuffered I/O
36 if (n >= ftp->max_response) {
37 ftp->max_response = ftp->max_response? ftp->max_response<<1 : 256;
38 ftp->response = realloc(ftp->response, ftp->max_response);
40 ftp->response[n++] = c;
42 if (n >= 4 && isdigit(ftp->response[0]) && isdigit(ftp->response[1]) && isdigit(ftp->response[2])
43 && ftp->response[3] != '-') break;
49 ftp->response[n-2] = 0;
50 return strtol(ftp->response, &p, 0);
53 static int kftp_send_cmd(knetFile *ftp, const char *cmd, int is_get)
55 if (socket_wait(ftp->ctrl_fd, 0) <= 0) return -1; // socket is not ready for writing
56 write(ftp->ctrl_fd, cmd, strlen(cmd));
57 return is_get? kftp_get_response(ftp) : 0;
60 static int kftp_pasv_prep(knetFile *ftp)
64 kftp_send_cmd(ftp, "PASV\r\n", 1);
65 for (p = ftp->response; *p && *p != '('; ++p);
66 if (*p != '(') return -1;
68 sscanf(p, "%d,%d,%d,%d,%d,%d", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]);
69 memcpy(ftp->pasv_ip, v, 4 * sizeof(int));
70 ftp->pasv_port = (v[4]<<8&0xff00) + v[5];
74 static int kftp_pasv_connect(knetFile *ftp)
76 #define __err_pasv_connect(func) do { perror(func); freeaddrinfo(res); return -1; } while (0)
78 struct addrinfo hints, *res;
79 struct linger lng = { 0, 0 };
81 char host[80], port[10];
83 if (ftp->pasv_port == 0) {
84 fprintf(stderr, "[kftp_pasv_connect] kftp_pasv_prep() is not called before hand.\n");
87 memset(&hints, 0, sizeof(struct addrinfo));
88 hints.ai_family = AF_UNSPEC;
89 hints.ai_socktype = SOCK_STREAM;
90 sprintf(host, "%d.%d.%d.%d", ftp->pasv_ip[0], ftp->pasv_ip[1], ftp->pasv_ip[2], ftp->pasv_ip[3]);
91 sprintf(port, "%d", ftp->pasv_port);
92 if (getaddrinfo(host, port, &hints, &res) != 0) { perror("getaddrinfo"); return -1; }
93 if ((ftp->fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) __err_pasv_connect("socket");
94 if (setsockopt(ftp->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) __err_pasv_connect("setsockopt");
95 if (setsockopt(ftp->fd, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1) __err_pasv_connect("setsockopt");
96 if (connect(ftp->fd, res->ai_addr, res->ai_addrlen) != 0) __err_pasv_connect("connect");
101 int kftp_connect(knetFile *ftp)
103 #define __err_connect(func) do { perror(func); return -1; } while (0)
107 struct addrinfo hints, *res;
108 memset(&hints, 0, sizeof(struct addrinfo));
109 hints.ai_family = AF_UNSPEC;
110 hints.ai_socktype = SOCK_STREAM;
111 if (getaddrinfo(ftp->host, "21", &hints, &res) != 0) __err_connect("getaddrinfo");
112 if ((ftp->ctrl_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) __err_connect("socket");
113 if (setsockopt(ftp->ctrl_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) __err_connect("setsockopt");
114 if (connect(ftp->ctrl_fd, res->ai_addr, res->ai_addrlen) != 0) __err_connect("connect");
116 kftp_get_response(ftp);
119 kftp_send_cmd(ftp, "USER anonymous\r\n", 1);
120 kftp_send_cmd(ftp, "PASS kftp@\r\n", 1);
121 kftp_send_cmd(ftp, "TYPE I\r\n", 1);
126 int kftp_reconnect(knetFile *ftp)
128 if (ftp->ctrl_fd >= 0) {
133 return kftp_connect(ftp);
136 // initialize ->type, ->host and ->retr
137 knetFile *kftp_parse_url(const char *fn, const char *mode)
142 if (strstr(fn, "ftp://") != fn) return 0;
143 for (p = (char*)fn + 6; *p && *p != '/'; ++p);
144 if (*p != '/') return 0;
146 fp = calloc(1, sizeof(knetFile));
147 fp->type = KNF_TYPE_FTP;
149 fp->host = calloc(l + 1, 1);
150 if (strchr(mode, 'c')) fp->no_reconnect = 1;
151 strncpy(fp->host, fn + 6, l);
152 fp->retr = calloc(strlen(p) + 8, 1);
153 sprintf(fp->retr, "RETR %s\r\n", p);
154 fp->seek_offset = -1;
157 // place ->fd at offset off
158 int kftp_connect_file(knetFile *fp)
163 if (fp->no_reconnect) kftp_get_response(fp);
168 sprintf(tmp, "REST %lld\r\n", (long long)fp->offset);
169 kftp_send_cmd(fp, tmp, 1);
171 kftp_send_cmd(fp, fp->retr, 0);
172 kftp_pasv_connect(fp);
173 ret = kftp_get_response(fp);
175 fprintf(stderr, "[kftp_connect_file] %s\n", fp->response);
184 knetFile *knet_open(const char *fn, const char *mode)
187 if (mode[0] != 'r') {
188 fprintf(stderr, "[kftp_open] only mode \"r\" is supported.\n");
191 if (strstr(fn, "ftp://") == fn) {
192 fp = kftp_parse_url(fn, mode);
193 if (fp == 0) return 0;
194 if (kftp_connect(fp) == -1) {
198 kftp_connect_file(fp);
204 int fd = open(fn, O_RDONLY);
209 fp = (knetFile*)calloc(1, sizeof(knetFile));
210 fp->type = KNF_TYPE_LOCAL;
216 knetFile *knet_dopen(int fd, const char *mode)
218 knetFile *fp = (knetFile*)calloc(1, sizeof(knetFile));
219 fp->type = KNF_TYPE_LOCAL;
224 off_t knet_read(knetFile *fp, void *buf, off_t len)
227 if (fp->fd < 0) return 0;
228 if (fp->type == KNF_TYPE_LOCAL) {
229 off_t rest = len, curr;
231 curr = read(fp->fd, buf + l, rest);
232 if (curr == 0) break;
233 l += curr; rest -= curr;
237 off_t rest = len, curr;
238 if (fp->is_ready == 0) {
239 if (!fp->no_reconnect) kftp_reconnect(fp);
240 kftp_connect_file(fp);
244 if (socket_wait(fp->fd, 1) <= 0) break; // socket is not ready for reading
245 curr = read(fp->fd, buf + l, rest);
246 if (curr == 0) break; // FIXME: end of file or bad network? I do not know...
247 l += curr; rest -= curr;
254 int knet_seek(knetFile *fp, off_t off, int whence)
256 if (fp->type == KNF_TYPE_LOCAL) {
257 if (lseek(fp->fd, off, whence) == -1) {
264 if (fp->type == KNF_TYPE_FTP) {
265 if (whence != SEEK_SET) { // FIXME: we can surely allow SEEK_CUR and SEEK_END in future
266 fprintf(stderr, "[knet_seek] only SEEK_SET is supported for FTP. Offset is unchanged.\n");
276 int knet_close(knetFile *fp)
278 if (fp == 0) return 0;
279 if (fp->ctrl_fd >= 0) close(fp->ctrl_fd);
280 if (fp->fd >= 0) close(fp->fd);
281 free(fp->response); free(fp->retr); free(fp->host);
291 // fp = knet_open("ftp://ftp.ncbi.nih.gov/1000genomes/ftp/data/NA12878/alignment/NA12878.chrom6.SLX.SRP000032.2009_06.bam", "r"); knet_seek(fp, 2500000000ll, SEEK_SET);
292 fp = knet_open("ftp://ftp.sanger.ac.uk/pub4/treefam/tmp/index.shtml", "r"); knet_seek(fp, 2000, SEEK_SET);
293 // fp = knet_open("knetfile.c", "r"); knet_seek(fp, 2000, SEEK_SET);
294 knet_read(fp, buf, 255);