1
|
|
2
|
#include <sys/types.h>
|
3
|
#include <sys/sbuf.h>
|
4
|
#include <sys/socket.h>
|
5
|
#include <sys/un.h>
|
6
|
|
7
|
#include <netinet/in.h>
|
8
|
#include <arpa/inet.h>
|
9
|
|
10
|
#include <err.h>
|
11
|
#include <errno.h>
|
12
|
#include <stdio.h>
|
13
|
#include <stdlib.h>
|
14
|
#include <string.h>
|
15
|
#include <strings.h>
|
16
|
#include <unistd.h>
|
17
|
#include <libgen.h>
|
18
|
#include <sys/utsname.h>
|
19
|
#include <time.h>
|
20
|
|
21
|
#include "fastcgi.h"
|
22
|
#include "common.h"
|
23
|
|
24
|
static int fcgisock = -1;
|
25
|
static struct sockaddr_un sun;
|
26
|
static struct sockaddr_in sin;
|
27
|
static int keepalive = 0;
|
28
|
|
29
|
static int
|
30
|
build_nvpair(struct sbuf *sb, const char *key, char *svalue)
|
31
|
{
|
32
|
int lkey, lvalue;
|
33
|
|
34
|
lkey = strlen(key);
|
35
|
lvalue = strlen(svalue);
|
36
|
|
37
|
if (lkey < 128)
|
38
|
sbuf_putc(sb, lkey);
|
39
|
else
|
40
|
sbuf_printf(sb, "%c%c%c%c", (u_char)((lkey >> 24) | 0x80), (u_char)((lkey >> 16) & 0xFF), (u_char)((lkey >> 16) & 0xFF), (u_char)(lkey & 0xFF));
|
41
|
|
42
|
if (lvalue < 128 || lvalue > 65535)
|
43
|
sbuf_putc(sb, lvalue);
|
44
|
else
|
45
|
sbuf_printf(sb, "%c%c%c%c", (u_char)((lvalue >> 24) | 0x80), (u_char)((lvalue >> 16) & 0xFF), (u_char)((lvalue >> 16) & 0xFF), (u_char)(lvalue & 0xFF));
|
46
|
|
47
|
if (lkey > 0)
|
48
|
sbuf_printf(sb, "%s", key);
|
49
|
if (lvalue > 0)
|
50
|
sbuf_printf(sb, "%s", svalue);
|
51
|
|
52
|
return (0);
|
53
|
}
|
54
|
|
55
|
static int
|
56
|
prepare_packet(FCGI_Header *header, int type, int lcontent, int requestId)
|
57
|
{
|
58
|
header->version = (unsigned char)FCGI_VERSION_1;
|
59
|
header->type = (unsigned char)type;
|
60
|
header->requestIdB1 = (unsigned char)((requestId >> 8) & 0xFF);
|
61
|
header->requestIdB0 = (unsigned char)(requestId & 0xFF);
|
62
|
header->contentLengthB1 = (unsigned char)((lcontent >> 8) & 0xFF);
|
63
|
header->contentLengthB0 = (unsigned char)(lcontent & 0xFF);
|
64
|
|
65
|
return (0);
|
66
|
}
|
67
|
|
68
|
static char *
|
69
|
read_packet(FCGI_Header *header, int sockfd)
|
70
|
{
|
71
|
char *buf = NULL;
|
72
|
int len, err;
|
73
|
|
74
|
memset(header, 0, sizeof(*header));
|
75
|
if (recv(sockfd, header, sizeof(*header), 0) > 0) {
|
76
|
len = (header->requestIdB1 << 8) + header->requestIdB0;
|
77
|
if (len != 1) {
|
78
|
return (NULL);
|
79
|
}
|
80
|
len = (header->contentLengthB1 << 8) + header->contentLengthB0;
|
81
|
len += header->paddingLength;
|
82
|
//printf("LEN: %d, %d, %d: %s\n", len, header->type, (header->requestIdB1 << 8) + header->requestIdB0, (char *)header);
|
83
|
if (len > 0) {
|
84
|
buf = calloc(1, len);
|
85
|
if (buf == NULL)
|
86
|
return (NULL);
|
87
|
|
88
|
err = recv(sockfd, buf, len, 0);
|
89
|
if (err < 0) {
|
90
|
free(buf);
|
91
|
return (NULL);
|
92
|
}
|
93
|
}
|
94
|
}
|
95
|
|
96
|
return (buf);
|
97
|
}
|
98
|
|
99
|
static void
|
100
|
usage()
|
101
|
{
|
102
|
printf("Usage: fcgicli [-d key=value] -f phpfiletocall -s phpfcgisocket -o [POST|GET]\n");
|
103
|
exit(-10);
|
104
|
}
|
105
|
|
106
|
int
|
107
|
main(int argc, char **argv)
|
108
|
{
|
109
|
FCGI_BeginRequestRecord *bHeader;
|
110
|
FCGI_Header *tmpl, rHeader;
|
111
|
struct sbuf *sbtmp2, *sbtmp;
|
112
|
struct utsname uts;
|
113
|
int ch, ispost = 0, len, result;
|
114
|
char *data = NULL, *script = NULL, *socketpath, *mtype = NULL, *buf;
|
115
|
|
116
|
tzset();
|
117
|
|
118
|
socketpath = (char *)FCGI_SOCK_PATH;
|
119
|
|
120
|
while ((ch = getopt(argc, argv, "d:f:ks:o:")) != -1) {
|
121
|
switch (ch) {
|
122
|
case 'd':
|
123
|
data = optarg;
|
124
|
break;
|
125
|
case 'f':
|
126
|
script = optarg;
|
127
|
break;
|
128
|
case 'k':
|
129
|
keepalive = 1;
|
130
|
break;
|
131
|
case 's':
|
132
|
socketpath = optarg;
|
133
|
break;
|
134
|
case 'o':
|
135
|
if (!strcasecmp(optarg, "POST"))
|
136
|
ispost = 1;
|
137
|
else if(!strcasecmp(optarg, "GET"))
|
138
|
ispost = 0;
|
139
|
else
|
140
|
usage();
|
141
|
|
142
|
mtype = optarg;
|
143
|
break;
|
144
|
}
|
145
|
}
|
146
|
argc -= optind;
|
147
|
argv += optind;
|
148
|
|
149
|
if (socketpath == NULL) {
|
150
|
printf("-s option is mandatory\n");
|
151
|
usage();
|
152
|
}
|
153
|
if (script == NULL) {
|
154
|
printf("-f option is mandatory\n");
|
155
|
usage();
|
156
|
}
|
157
|
if (data != NULL && ispost) {
|
158
|
printf("-d option is useful only with POST operation\n");
|
159
|
usage();
|
160
|
}
|
161
|
|
162
|
if (strstr(socketpath, "/")) {
|
163
|
fcgisock = socket(PF_UNIX, SOCK_STREAM, 0);
|
164
|
if (fcgisock < 0)
|
165
|
err(-2, "could not create socket.");
|
166
|
|
167
|
bzero(&sun, sizeof(sun));
|
168
|
sun.sun_family = AF_LOCAL;
|
169
|
strlcpy(sun.sun_path, socketpath, sizeof(sun.sun_path));
|
170
|
len = sizeof(sun);
|
171
|
|
172
|
//alarm(3); /* Wait 3 seconds to complete a connect. More than enough?! */
|
173
|
if (connect(fcgisock, (struct sockaddr *)&sun, len) < 0)
|
174
|
errx(errno, "Could not connect to server(%s).", socketpath);
|
175
|
} else {
|
176
|
char *host, *port;
|
177
|
if (!(port = strstr(socketpath, ":")))
|
178
|
errx(-1, "Need the port specified as host:port");
|
179
|
|
180
|
*port++ = '\0';
|
181
|
host = socketpath;
|
182
|
|
183
|
fcgisock = socket(PF_INET, SOCK_STREAM, 0);
|
184
|
if (fcgisock < 0)
|
185
|
err(-2, "could not create socket.");
|
186
|
|
187
|
bzero(&sin, sizeof(sin));
|
188
|
sin.sin_family = AF_INET;
|
189
|
inet_pton(AF_INET, host, &sin.sin_addr);
|
190
|
sin.sin_port = htons(atoi(port));
|
191
|
len = sizeof(sin);
|
192
|
|
193
|
//alarm(3); /* Wait 3 seconds to complete a connect. More than enough?! */
|
194
|
if (connect(fcgisock, (struct sockaddr *)&sin, len) < 0)
|
195
|
errx(errno, "Could not connect to server(%s:%s).", host, port);
|
196
|
}
|
197
|
|
198
|
uname(&uts);
|
199
|
|
200
|
if (ispost) {
|
201
|
printf("POST is not yet implemented\n");
|
202
|
tmpl = NULL;
|
203
|
|
204
|
} else {
|
205
|
sbtmp2 = sbuf_new_auto();
|
206
|
if (sbtmp2 == NULL)
|
207
|
errx(-3, "Could not allocate memory\n");
|
208
|
build_nvpair(sbtmp2, "GATEWAY_INTERFACE", (char *)"FastCGI/1.0");
|
209
|
build_nvpair(sbtmp2, "REQUEST_METHOD", (char *)"GET");
|
210
|
build_nvpair(sbtmp2, "NO_HEADERS", (char *)"1");
|
211
|
sbtmp = sbuf_new_auto();
|
212
|
sbuf_printf(sbtmp, "/%s", basename(script));
|
213
|
sbuf_finish(sbtmp);
|
214
|
build_nvpair(sbtmp2, "SCRIPT_FILENAME", script);
|
215
|
build_nvpair(sbtmp2, "SCRIPT_NAME", sbuf_data(sbtmp));
|
216
|
if (data == NULL) {
|
217
|
build_nvpair(sbtmp2, "REQUEST_URI", sbuf_data(sbtmp));
|
218
|
}
|
219
|
build_nvpair(sbtmp2, "DOCUMENT_URI", sbuf_data(sbtmp));
|
220
|
sbuf_delete(sbtmp);
|
221
|
if (data) {
|
222
|
build_nvpair(sbtmp2, "QUERY_STRING", data);
|
223
|
sbtmp = sbuf_new_auto();
|
224
|
sbuf_printf(sbtmp, "/%s?%s", basename(script), data);
|
225
|
sbuf_finish(sbtmp);
|
226
|
build_nvpair(sbtmp2, "REQUEST_URI", sbuf_data(sbtmp));
|
227
|
sbuf_delete(sbtmp);
|
228
|
}
|
229
|
sbuf_finish(sbtmp2);
|
230
|
|
231
|
len = (3 * sizeof(FCGI_Header)) + sizeof(FCGI_BeginRequestRecord) + sbuf_len(sbtmp2);
|
232
|
buf = calloc(1, len);
|
233
|
if (buf == NULL)
|
234
|
errx(-4, "Cannot allocate memory");
|
235
|
|
236
|
bHeader = (FCGI_BeginRequestRecord *)buf;
|
237
|
prepare_packet(&bHeader->header, FCGI_BEGIN_REQUEST, sizeof(bHeader->body), 1);
|
238
|
bHeader->body.roleB0 = (unsigned char)FCGI_RESPONDER;
|
239
|
bHeader->body.flags = (unsigned char)(keepalive ? FCGI_KEEP_CONN : 0);
|
240
|
bHeader++;
|
241
|
tmpl = (FCGI_Header *)bHeader;
|
242
|
prepare_packet(tmpl, FCGI_PARAMS, sbuf_len(sbtmp2), 1);
|
243
|
tmpl++;
|
244
|
memcpy((char *)tmpl, sbuf_data(sbtmp2), sbuf_len(sbtmp2));
|
245
|
tmpl = (FCGI_Header *)(((char *)tmpl) + sbuf_len(sbtmp2));
|
246
|
sbuf_delete(sbtmp2);
|
247
|
}
|
248
|
prepare_packet(tmpl, FCGI_PARAMS, 0, 1);
|
249
|
tmpl++;
|
250
|
prepare_packet(tmpl, FCGI_STDIN, 0, 1);
|
251
|
while (len > 0) {
|
252
|
result = write(fcgisock, buf, len);
|
253
|
if (result < 0) {
|
254
|
printf("Something wrong happened while sending request\n");
|
255
|
free(buf);
|
256
|
close(fcgisock);
|
257
|
exit(-2);
|
258
|
}
|
259
|
len -= result;
|
260
|
buf += result;
|
261
|
}
|
262
|
free(buf);
|
263
|
|
264
|
do {
|
265
|
buf = read_packet(&rHeader, fcgisock);
|
266
|
if (buf == NULL) {
|
267
|
printf("Something wrong happened while reading request\n");
|
268
|
close(fcgisock);
|
269
|
exit(-1);
|
270
|
}
|
271
|
switch (rHeader.type) {
|
272
|
case FCGI_DATA:
|
273
|
case FCGI_STDOUT:
|
274
|
case FCGI_STDERR:
|
275
|
printf("%s", buf);
|
276
|
free(buf);
|
277
|
break;
|
278
|
case FCGI_ABORT_REQUEST:
|
279
|
printf("Request aborted\n");
|
280
|
free(buf);
|
281
|
goto endprog;
|
282
|
break;
|
283
|
case FCGI_END_REQUEST:
|
284
|
switch (((FCGI_EndRequestBody *)buf)->protocolStatus) {
|
285
|
case FCGI_CANT_MPX_CONN:
|
286
|
printf("The FCGI server cannot multiplex\n");
|
287
|
break;
|
288
|
case FCGI_OVERLOADED:
|
289
|
printf("The FCGI server is overloaded\n");
|
290
|
break;
|
291
|
case FCGI_UNKNOWN_ROLE:
|
292
|
printf("FCGI role is unknown\n");
|
293
|
break;
|
294
|
case FCGI_REQUEST_COMPLETE:
|
295
|
break;
|
296
|
}
|
297
|
free(buf);
|
298
|
goto endprog;
|
299
|
break;
|
300
|
}
|
301
|
} while (rHeader.type != FCGI_END_REQUEST);
|
302
|
|
303
|
endprog:
|
304
|
close(fcgisock);
|
305
|
|
306
|
return (0);
|
307
|
}
|