1 /* This is a simple TCP server that listens on port 1234 and provides lists
2  * of files to clients, using a protocol defined in file_server.proto.
3  *
4  * It directly deserializes and serializes messages from network, minimizing
5  * memory use.
6  *
7  * For flexibility, this example is implemented using posix api.
8  * In a real embedded system you would typically use some other kind of
9  * a communication and filesystem layer.
10  */
11 
12 #include <sys/socket.h>
13 #include <sys/types.h>
14 #include <netinet/in.h>
15 #include <unistd.h>
16 #include <dirent.h>
17 #include <stdio.h>
18 #include <string.h>
19 
20 #include <pb_encode.h>
21 #include <pb_decode.h>
22 
23 #include "fileproto.pb.h"
24 #include "common.h"
25 
26 /* This callback function will be called during the encoding.
27  * It will write out any number of FileInfo entries, without consuming unnecessary memory.
28  * This is accomplished by fetching the filenames one at a time and encoding them
29  * immediately.
30  */
ListFilesResponse_callback(pb_istream_t * istream,pb_ostream_t * ostream,const pb_field_iter_t * field)31 bool ListFilesResponse_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field)
32 {
33     PB_UNUSED(istream);
34     if (ostream != NULL && field->tag == ListFilesResponse_file_tag)
35     {
36         DIR *dir = *(DIR**)field->pData;
37         struct dirent *file;
38         FileInfo fileinfo = {};
39 
40         while ((file = readdir(dir)) != NULL)
41         {
42             fileinfo.inode = file->d_ino;
43             strncpy(fileinfo.name, file->d_name, sizeof(fileinfo.name));
44             fileinfo.name[sizeof(fileinfo.name) - 1] = '\0';
45 
46             /* This encodes the header for the field, based on the constant info
47             * from pb_field_t. */
48             if (!pb_encode_tag_for_field(ostream, field))
49                 return false;
50 
51             /* This encodes the data for the field, based on our FileInfo structure. */
52             if (!pb_encode_submessage(ostream, FileInfo_fields, &fileinfo))
53                 return false;
54         }
55 
56         /* Because the main program uses pb_encode_delimited(), this callback will be
57         * called twice. Rewind the directory for the next call. */
58         rewinddir(dir);
59     }
60 
61     return true;
62 }
63 
64 /* Handle one arriving client connection.
65  * Clients are expected to send a ListFilesRequest, terminated by a '0'.
66  * Server will respond with a ListFilesResponse message.
67  */
handle_connection(int connfd)68 void handle_connection(int connfd)
69 {
70     DIR *directory = NULL;
71 
72     /* Decode the message from the client and open the requested directory. */
73     {
74         ListFilesRequest request = {};
75         pb_istream_t input = pb_istream_from_socket(connfd);
76 
77         if (!pb_decode_delimited(&input, ListFilesRequest_fields, &request))
78         {
79             printf("Decode failed: %s\n", PB_GET_ERROR(&input));
80             return;
81         }
82 
83         directory = opendir(request.path);
84         printf("Listing directory: %s\n", request.path);
85     }
86 
87     /* List the files in the directory and transmit the response to client */
88     {
89         ListFilesResponse response = {};
90         pb_ostream_t output = pb_ostream_from_socket(connfd);
91 
92         if (directory == NULL)
93         {
94             perror("opendir");
95 
96             /* Directory was not found, transmit error status */
97             response.has_path_error = true;
98             response.path_error = true;
99         }
100         else
101         {
102             /* Directory was found, transmit filenames */
103             response.has_path_error = false;
104             response.file = directory;
105         }
106 
107         if (!pb_encode_delimited(&output, ListFilesResponse_fields, &response))
108         {
109             printf("Encoding failed: %s\n", PB_GET_ERROR(&output));
110         }
111     }
112 
113     if (directory != NULL)
114         closedir(directory);
115 }
116 
main(int argc,char ** argv)117 int main(int argc, char **argv)
118 {
119     int listenfd, connfd;
120     struct sockaddr_in servaddr;
121     int reuse = 1;
122 
123     /* Listen on localhost:1234 for TCP connections */
124     listenfd = socket(AF_INET, SOCK_STREAM, 0);
125     setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
126 
127     memset(&servaddr, 0, sizeof(servaddr));
128     servaddr.sin_family = AF_INET;
129     servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
130     servaddr.sin_port = htons(1234);
131     if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0)
132     {
133         perror("bind");
134         return 1;
135     }
136 
137     if (listen(listenfd, 5) != 0)
138     {
139         perror("listen");
140         return 1;
141     }
142 
143     for(;;)
144     {
145         /* Wait for a client */
146         connfd = accept(listenfd, NULL, NULL);
147 
148         if (connfd < 0)
149         {
150             perror("accept");
151             return 1;
152         }
153 
154         printf("Got connection.\n");
155 
156         handle_connection(connfd);
157 
158         printf("Closing connection.\n");
159 
160         close(connfd);
161     }
162 
163     return 0;
164 }
165