summaryrefslogtreecommitdiffstats
path: root/tcpclient.c
blob: f2a2fc7a5f1ea62ab38846d38f82fe1deb373ba1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/* tcpclient.c
 *
 * TCP Client
 * note: 'echo "hello" | nc -l -p 8080 localhost'
 *
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define TARGET_MAX_SZ 1*1024*1024

static int sockfd;
static struct addrinfo *res;
static char *recv_buff;

void cleanup(void)
{
    shutdown(sockfd, SHUT_RDWR);
    close(sockfd);

    if (recv_buff)
        free(recv_buff);

    /* free the linked list */
    if (res)
        freeaddrinfo(res);
}

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        fprintf(stderr, "usage: %s <host>\n", argv[0]);
        exit(-1);
    }
    if (!strstr(argv[1], "http://"))
    {
        fprintf(stderr, "host needs to prefixed with \"http://\" protocol\n");
        exit(-1);
    }

    /* grab the host and path from the URL */
    char hoststr[100] = { 0 }, pathstr[100] = { 0 }, filestr[100] = { 0 };
    sscanf(argv[1], "http://%[^/]/%s", hoststr, pathstr);
    printf("host is: \"%s\"\n"
           "path is: \"%s\"\n", hoststr, pathstr);

    /* parse out a filestrname */
    char *filepart = strrchr(pathstr, '/');
    if (filepart && strlen(filepart + 1))
        strcpy(filestr, filepart + 1);
    else
        strcpy(filestr, "index.html");
    printf("file is: \"%s\"\n", filestr);

    int ret;

    /* prepare for getaddrinfo call */
    struct addrinfo hints;
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ((ret = getaddrinfo(hoststr, "http", &hints, &res)) != 0)
    {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
        exit(-1);
    }

    /* try all available IP addresses */
    struct addrinfo *p;
    char ipstr[INET6_ADDRSTRLEN];
    void *addr;
    char *ipver;
    struct sockaddr_in *ipv4;
#if 0
    struct sockaddr_in6 *ipv6;
#endif

    printf("trying all available IP addresses for %s:\n", hoststr);
    for (p = res; p != NULL; p = p->ai_next)
    {
        /* get the pointer to the address itself,
         * different fields in IPv4 and IPv6 */
        if (p->ai_family == AF_INET)
        {   /* IPv4 */
            ipv4 = (struct sockaddr_in *) p->ai_addr;
            addr = &(ipv4->sin_addr);
            ipver = "IPv4";
        }
        else
            continue; /* ignore IPv6 for now */
#if 0
        else
        {   /* IPv6 */
            ipv6 = (struct sockaddr_in6 *) p->ai_addr;
            addr = &(ipv6->sin6_addr);
            ipver = "IPv6";
        }
#endif

        /* convert the IP to a string and print it */
        inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr));
        printf("attempting to connect to %s: %s\n", ipstr, ipver);

        /* create a socket */
        if ((sockfd = socket(p->ai_family, p->ai_socktype, res->ai_protocol)) == -1)
        {
            perror("socket");
            close(sockfd);
        }

        /* attempt to connect */
        if (connect(sockfd, (struct sockaddr *) ipv4, sizeof(*ipv4)) == -1)
            perror("connect");
        else
        {
            printf("successfully connected\n");
            break; /* break out of a loop, we got a connection */
        }
    }

    if (!p)
    {
        fprintf(stderr, "failed to connect to a host\n");
        exit(-1);
    }

    atexit(cleanup);

    /* send http commands and receive data */
    char request[128] = { 0 };
    sprintf(request, "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", pathstr, hoststr);
    size_t request_len = strlen(request) * sizeof(char);

    ret = send(sockfd, (void *) request, request_len, 0);
    if (ret == -1)
    {
        perror("send");
        exit(-1);
    }
    else if (ret != request_len)
    {
        fprintf(stderr, "sent less bytes than requested\n");
        exit(-1);
    }

    /* receive until connection gets closed */
    recv_buff = (char *) malloc(sizeof(char) * TARGET_MAX_SZ);
    if (!recv_buff)
    {
        perror("malloc");
        exit(-1);
    }
    size_t recv_len = 0;

    do
    {
        ret = recv(sockfd, recv_buff + recv_len, TARGET_MAX_SZ - recv_len, 0);
        if (ret == -1)
        {
            perror("recv");
            exit(-1);
        }
        else if (ret == 0)
            printf("remote host has closed the connection, received %lu bytes\n", recv_len);

        recv_len += ret;

    } while (ret > 0);

    if (recv_len < 1)
    {
        fprintf(stderr, "did not receive any data from the host!\n");
        exit(-1);
    }

    /* poke at http header and check for errors */
    int code;
    sscanf(recv_buff, "HTTP/1.%*[01] %d ", &code);
    if (code == 200)
        printf("received file\n");
    else
    {
        printf("got an error code %d\n", code);
        char errorline[100];
        sscanf(recv_buff, "%[^\r\n]", errorline);
        fprintf(stderr, "%s\n", errorline);
        exit(-1);
    }

    /* save to disk */
    char *buff_p = strstr(recv_buff, "\r\n\r\n"); /* seek past http header */
    if (buff_p)
    {
        buff_p += 4;

        FILE *fp = fopen(filestr, "w");
        if (!fp)
        {
            perror("fopen");
            exit(-1);
        }

        ret = fwrite(buff_p, recv_len - (buff_p - recv_buff), sizeof(char), fp);
        fclose(fp);
    }
    else
        fprintf(stderr, "could not seek past http header\n");
}