1 /*
2  * Copyright (C) 2017-2019 Alibaba Group Holding Limited
3  */
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <lwip/def.h>
10 #include <lwip/netdb.h>
11 #include <lwip/sockets.h>
12 #include <lwip/apps/sendfile.h>
13 
14 #define DATALEN 1024
15 #define MAXSIZE 32
16 #define PATHMAX 64
17 static int sendfile_server_task_started = 0;
18 
19 void sendfile_server_task_create(char *port);
20 
sendfile(int out_fd,int in_fd,off_t * offset,size_t count)21 ssize_t sendfile(int out_fd, int in_fd, off_t* offset, size_t count)
22 {
23     char data[DATALEN];
24     int writelen;
25     int readlen;
26     int curlen = 0;
27 
28     while(1) {
29         memset(data, 0, sizeof(data));
30         readlen = read(in_fd, data, sizeof(data));
31 
32         if(0 == readlen) {
33             LWIP_DEBUGF( SENDFILE_DEBUG, ("%s:%d read finished", __func__, __LINE__));
34             break;
35         }
36 
37         if(readlen < 0) {
38             if((EAGAIN != errno) && (EINTR != errno) && (EINPROGRESS != errno)) {
39                 LWIP_DEBUGF( SENDFILE_DEBUG, ("%s:%d readlen %d errno: %d out_fd %d in_fd %d", __func__, __LINE__, readlen, errno, out_fd, in_fd));
40                 return -1;
41             } else {
42                 aos_msleep(100);
43                 continue;
44             }
45         }
46 
47         if(readlen > sizeof(data)) {
48             LWIP_DEBUGF( SENDFILE_DEBUG, ("%s:%d read len %d is large than data len %d",
49                          __func__, __LINE__, readlen ,sizeof(data)));
50             return -1;
51         }
52 
53         if((NULL != offset) && (curlen < *offset) && (curlen + readlen > *offset)) {
54             curlen += readlen;
55 
56             writelen = write(out_fd, data + *offset - curlen, curlen - *offset);
57             if (((writelen < 0)&&(EAGAIN != errno) && (EINTR != errno) && (EINPROGRESS != errno))
58                    || (writelen != readlen)) {
59                 LWIP_DEBUGF( SENDFILE_DEBUG, ("sendfile failed out_fd %d in_fd %d", out_fd, in_fd));
60                 return -1;
61             }
62         }
63         else if((NULL == offset) || (curlen + readlen > *offset)) {
64             curlen += readlen;
65             writelen = write(out_fd, data, readlen);
66             if (((writelen < 0)&&(EAGAIN != errno) && (EINTR != errno) && (EINPROGRESS != errno))
67                    || (writelen != readlen)) {
68                 LWIP_DEBUGF( SENDFILE_DEBUG, ("sendfile write failed out_fd %d errno %d", out_fd, errno));
69                 return -1;
70             }
71         }
72         aos_msleep(100);
73     }
74     return curlen;
75 }
76 
sendfile_client(int argc,char * argv[])77 int sendfile_client(int argc,char *argv[])
78 {
79     int fd, sockfd;
80     char buf[MAXSIZE];
81     int mode, size;
82     struct sockaddr_in srvaddr;
83     char * dst_addr = NULL;
84     char * dst_port = NULL;
85     char * src_file = NULL;
86     char * file_name = NULL;
87     int i;
88     int ret = -1;
89 
90     for(i = 1; i< argc; i++) {
91         if(0 == strcmp(argv[i], "-d")) {
92             i++;
93             if(i >= argc) {
94                 LWIP_DEBUGF( SENDFILE_DEBUG, ("%s:%d Invalid command", __func__, __LINE__));
95                 goto exit;
96             }
97             dst_addr = argv[i];
98         }
99         else if(0 == strcmp(argv[i], "-p")) {
100             i++;
101             if(i >= argc) {
102                 LWIP_DEBUGF( SENDFILE_DEBUG, ("%s:%d Invalid command", __func__, __LINE__));
103                 goto exit;
104             }
105             dst_port = argv[i];
106         }
107         else if(0 == strcmp(argv[i], "-f")) {
108             i++;
109             if(i >= argc) {
110                 LWIP_DEBUGF( SENDFILE_DEBUG, ("%s:%d Invalid command", __func__, __LINE__));
111                 goto exit;
112             }
113             src_file = argv[i];
114         }
115         else if(0 == strcmp(argv[i], "-n")) {
116             i++;
117             if(i >= argc) {
118                 LWIP_DEBUGF( SENDFILE_DEBUG, ("%s:%d Invalid command", __func__, __LINE__));
119                 goto exit;
120             }
121             file_name = argv[i];
122         }
123     }
124 
125     if((NULL == dst_addr) || (NULL == dst_port) || (NULL == src_file) || (NULL == file_name)) {
126         LWIP_DEBUGF( SENDFILE_DEBUG, ("%s:%d Invalid command", __func__, __LINE__));
127         goto exit;
128     }
129 
130     sockfd = lwip_socket(AF_INET,SOCK_STREAM,0);
131     if(sockfd < 0) {
132         LWIP_DEBUGF( SENDFILE_DEBUG, ("%s:%d Invalid command", __func__, __LINE__));
133         goto exit;
134     }
135     bzero(&srvaddr,sizeof(srvaddr));
136     srvaddr.sin_family = AF_INET;
137     inet_pton(AF_INET, dst_addr, (struct sockaddr *)&(srvaddr.sin_addr));
138     srvaddr.sin_port = htons(atoi(dst_port));
139     ret = lwip_connect(sockfd, (struct sockaddr *)&srvaddr,sizeof(srvaddr));
140     if (ret == -1) {
141         lwip_close(sockfd);
142         LWIP_DEBUGF( SENDFILE_DEBUG, ("connect failed: %s", strerror(errno)));
143         goto exit;
144     }
145 
146     /* write file name to server */
147     ret = lwip_write(sockfd, src_file, strlen(src_file));
148     if (ret == -1) {
149         lwip_close(sockfd);
150         LWIP_DEBUGF( SENDFILE_DEBUG, ("write failed: %s", strerror(errno)));
151         goto exit;
152     }
153 
154     /* get the file mode/size from server */
155     memset(buf, 0, sizeof(buf));
156     ret = lwip_recv(sockfd, buf, sizeof(buf), 0);
157     if (ret == -1) {
158         lwip_close(sockfd);
159         LWIP_DEBUGF( SENDFILE_DEBUG, ("recv failed: %s", strerror(errno)));
160         goto exit;
161     }
162 
163     if(2 != sscanf(buf, "mode=%d size=%d\n", &mode, &size)) {
164         lwip_close(sockfd);
165         LWIP_DEBUGF( SENDFILE_DEBUG, ("wrong mode and size input: %s", strerror(errno)));
166         goto exit;
167     }
168 
169     fd = open(file_name, O_WRONLY|O_CREAT, mode);
170     if(fd < 0) {
171         LWIP_DEBUGF( SENDFILE_DEBUG, ("open file %s failed: %s", file_name, strerror(errno)));
172         lwip_close(sockfd);
173         goto exit;
174     }
175 
176     while(1) {
177         ret = sendfile(fd, sockfd, 0, size);
178         if((ret < 0) && ((EAGAIN != errno) && (EINTR != errno) && (EINPROGRESS != errno))) {
179             LWIP_DEBUGF( SENDFILE_DEBUG, ("sendfile ret %d, %s", ret, strerror(errno)));
180             break;
181         }
182         if(ret == 0) {
183             LWIP_DEBUGF( SENDFILE_DEBUG, ("sendfile finished"));
184             break;
185         }
186     }
187 
188     lwip_close(fd);
189     lwip_close(sockfd);
190 exit:
191     return ret;
192 }
193 
sendfile_server(int argc,char ** argv)194 int sendfile_server(int argc, char **argv)
195 {
196     int i;
197     char* port = NULL;          /* port number to use */
198 
199     for(i = 1; i< argc; i++) {
200         if(0 == strcmp(argv[i], "-p")) {
201             i++;
202             if(i >= argc) {
203                 LWIP_DEBUGF( SENDFILE_DEBUG, ("%s:%d Invalid command", __func__, __LINE__));
204                 return -1;
205             }
206             port = argv[i];
207             if (atoi(port) <= 0) {
208                 LWIP_DEBUGF( SENDFILE_DEBUG, ("invalid port: %s", port));
209                 return -1;
210             }
211 	    }
212     }
213 
214     if (port == NULL) {
215         LWIP_DEBUGF( SENDFILE_DEBUG, ("No port input"));
216         return -1;
217     }
218     sendfile_server_task_create(port);
219     return 0;
220 }
221 
sendfile_server_thread(void * args)222 void sendfile_server_thread(void *args)
223 {
224     int sockfd;                	/* socket desciptor */
225     int connfd;                	/* file descriptor for socket */
226     socklen_t addrlen;		    /* argument to accept */
227     int ret;                    /* holds return code of system calls */
228     int fd;                    	/* file descriptor for file to send */
229     int port = 0;               /* port number to use */
230 
231     char filename[PATHMAX]={0}; /* filename to send */
232     char buf[MAXSIZE];
233     off_t offset = 0;          	/* file offset */
234     struct stat stat_buf;      	/* argument to fstat */
235 
236     struct sockaddr_in addrserver;   	/* socket parameters for bind ipv4*/
237     struct sockaddr_in addrclient;  	/* socket parameters for accept ipv4*/
238 
239     port = atoi((char *)args);
240 
241     /* create Internet domain socket */
242     sockfd = lwip_socket(AF_INET, SOCK_STREAM, 0);
243     if (sockfd == -1) {
244         LWIP_DEBUGF( SENDFILE_DEBUG, ("unable to create socket: %s", strerror(errno)));
245         goto exit;
246     }
247 
248     /* fill in socket structure */
249     bzero(&addrserver, sizeof(addrserver));
250     addrserver.sin_family = AF_INET;
251     addrserver.sin_addr.s_addr = INADDR_ANY;
252     addrserver.sin_port = htons(port);
253 
254     /* bind socket to the port */
255     ret =  lwip_bind(sockfd, (struct sockaddr *)&addrserver, sizeof(addrserver));
256     if (ret == -1) {
257         LWIP_DEBUGF( SENDFILE_DEBUG, ("unable to bind to socket: %s", strerror(errno)));
258         goto exit;
259     }
260 
261     /* listen for clients on the socket */
262     ret = lwip_listen(sockfd, 1);
263     if (ret == -1) {
264         LWIP_DEBUGF( SENDFILE_DEBUG, ("listen failed: %s", strerror(errno)));
265         goto exit;
266     }
267 
268     for ( ;; ) {
269         addrlen = sizeof(addrclient);
270         /* wait for a client to connect */
271         connfd = lwip_accept(sockfd, (struct sockaddr *)&addrclient, &addrlen);
272         if (connfd == -1) {
273             LWIP_DEBUGF( SENDFILE_DEBUG, ("accept failed: %s", strerror(errno)));
274             goto exit;
275         }
276 
277         /* get the file name from the client */
278         ret = lwip_recv(connfd, filename, sizeof(filename), 0);
279         if (ret == -1) {
280             LWIP_DEBUGF( SENDFILE_DEBUG, ("recv failed: %s", strerror(errno)));
281 	        lwip_close(connfd);
282             goto exit;
283         }
284 
285         /* null terminate and strip any \r and \n from filename */
286         filename[ret] = '\0';
287         if ( (filename[strlen(filename)-1] == '\n') || \
288             (filename[strlen(filename)-1] == '\r'))
289             filename[strlen(filename)-1] = '\0';
290 
291         /* exit server if filename is "quit" */
292         if (strcmp(filename, "quit") == 0) {
293             LWIP_DEBUGF( SENDFILE_DEBUG, ("quit command received, shutting down server"));
294 	        lwip_close(connfd);
295             break;
296         }
297 
298         LWIP_DEBUGF( SENDFILE_DEBUG, ("received request to send file %s", filename));
299 
300         /* open the file to be sent */
301         fd = open(filename, O_RDONLY);
302         if (fd < 0) {
303 	        lwip_close(connfd);
304             LWIP_DEBUGF( SENDFILE_DEBUG, ("unable to open '%s': %s", filename, strerror(errno)));
305             goto exit;
306         }
307 
308         /* get the size of the file to be sent */
309         ret = fstat(fd, &stat_buf);
310         if (ret == -1) {
311 	        lwip_close(connfd);
312             lwip_close(fd);
313             LWIP_DEBUGF( SENDFILE_DEBUG, ("unable to open '%s': %s", filename, strerror(errno)));
314             goto exit;
315         }
316         memset(buf, 0, sizeof(buf));
317         snprintf(buf, sizeof(buf), "mode=%d size=%ld\n", stat_buf.st_mode, stat_buf.st_size);
318         ret = lwip_write(connfd, buf, strlen(buf));
319         if (ret < 0) {
320             LWIP_DEBUGF( SENDFILE_DEBUG, ("error from sendfile: %s", strerror(errno)));
321 	        lwip_close(connfd);
322             lwip_close(fd);
323             goto exit;
324         }
325 
326         /* copy file using sendfile */
327         offset = 0;
328         ret = sendfile(connfd, fd, &offset, stat_buf.st_size);
329         if (ret < 0) {
330             LWIP_DEBUGF( SENDFILE_DEBUG, ("error from sendfile: %s", strerror(errno)));
331 	        lwip_close(connfd);
332             lwip_close(fd);
333             goto exit;
334         }
335 
336         if(ret == 0) {
337             lwip_close(connfd);
338             lwip_close(fd);
339             goto exit;
340         }
341 
342         if (ret != stat_buf.st_size) {
343             LWIP_DEBUGF( SENDFILE_DEBUG, ("incomplete transfer from sendfile: %d of %d bytes",
344 		  ret, (int)stat_buf.st_size));
345 	        lwip_close(connfd);
346             lwip_close(fd);
347             goto exit;
348         }
349 	    lwip_close(connfd);
350 	    lwip_close(fd);
351     }//for(;;)
352 
353 exit:
354     /* close socket */
355     if(sockfd != -1) {
356         lwip_close(sockfd);
357     }
358     sendfile_server_task_started = 0;
359 }
360 
sendfile_server_task_create(char * port)361 void sendfile_server_task_create(char *port)
362 {
363     if(1 != sendfile_server_task_started) {
364         aos_task_new("sendfile_server_task", sendfile_server_thread, port, 20 * 1024);
365         sendfile_server_task_started = 1;
366     }
367     else {
368         LWIP_DEBUGF( SENDFILE_DEBUG, ("http upload task is already running, create a new task should do previous exit first"));
369     }
370 }
371 
372