I'm trying to download a few seconds from a livestream using sockets.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> /* close() */
#include <sys/socket.h>
#include <netdb.h>
int main(void)
{
int sock;
char host[] = "http://141.138.89.176/fun-1-44-128";
char port[] = "80";
struct addrinfo hints, *res;
char message[] = "GET / HTTP/1.1\nHost: http://141.138.89.176/fun-1-44-128";
unsigned int i;
char buf[1024];
int bytes_read;
int status;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
status = getaddrinfo(host, port, &hints, &res);
if (status != 0) {
perror("getaddrinfo");
return 1;
}
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock == -1) {
perror("socket");
return 1;
}
status = connect(sock, res->ai_addr, res->ai_addrlen);
if (status == -1) {
perror("connect");
return 1;
}
freeaddrinfo(res);
send(sock, message, strlen(message), 0);
do {
bytes_read = recv(sock, buf, 1024, 0);
if (bytes_read == -1) {
perror("recv");
}
else {
printf("%.*s", bytes_read, buf);
}
} while (bytes_read > 0);
close(sock);
return 0;
}
The code compiles, however when running, getaddrinfo() fails. I assume that this means that the host cannot be found.
This is my url: http://141.138.89.176/fun-1-44-128
It works in my browser so I don't know what's going on. Could anyone shed some light on the problem?
I tested your code on my environment and it's perfectly working so your code is surely okay. I recommend checking your compiler, maybe reinstall it.
EDIT: Okay I think I found the problem. It was that the host should only be the ip address, not the full link.
(also added a fix to the error reporting from getaddrinfo)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> /* close() */
#include <sys/socket.h>
#include <netdb.h>
int main(void)
{
int sock;
char host[] = "141.138.89.176";
char port[] = "80";
struct addrinfo hints, *res;
char message[] = "GET / HTTP/1.1\nHost: 141.138.89.176/fun-1-44-128";
unsigned int i;
char buf[1024];
int bytes_read;
int status;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
status = getaddrinfo(host, port, &hints, &res);
if (status != 0) {
printf("Code: %d\n", status);
printf("Message: %s\n", gai_strerror(status));
return 1;
}
...
OK I figured out the solution.
getaddrinfo() only accepts base host names, domain names or ip addresses, with no subdirectories.
This means that 192.168.0.4, www.site.com, localhost are all valid. 192.168.0.4/Search, www.site.com/Search, localhost/Search are not.
You also don't need to include the scheme. Sockets don't differentiate http from https, it's how requests are dealt with makes the difference.
You need to change host so that it only contains the IP address.
What you want is:
char host[] = "141.138.89.176";
You won't get that error now.
If you want to access a subdirectory you pass it as a GET request. It's actually the web server that handles file and directory stuff. The base IP stays the same.
First off your formatting is wrong. It should be \r\n and \r\n\n\n to finish. It should look like this: char message[] = "GET / HTTP/1.1\r\nHost: 141.138.89.176/fun-1-44-128\r\n\n\n";
Now if you try this, you will get a 400 error. this is because it's incorrectly written. Much like the above Host only accepts the base url/ip. See that / right after the GET? That's where you request any subdirectories/files you want. The / you have there now indicates that you want the top level dictionary.
It should look like this:
message[] = "GET /fun-1-44-128 HTTP/1.1\r\nHost: 141.138.89.176\r\n\n\n";
Related
I'm sorry for such a big block of code, but I'm so lost as to what is happening, I have no idea where the problem might be...
I am trying to get a VERY minimal dtls server going and I can't get the client and server to finish handshaking.
Here is my code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <openssl/ssl.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/opensslv.h>
int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len)
{
cookie = "cookie";
cookie_len = 5;
return 1;
}
int verify_cookie(SSL *ssl, const unsigned char *cookie, unsigned int cookie_len)
{
return 1;
}
int dtls_verify_callback (int ok, X509_STORE_CTX *ctx) {
/* This function should ask the user
* if he trusts the received certificate.
* Here we always trust.
*/
return 1;
}
int main() {
char buff[FILENAME_MAX];
getcwd(buff, FILENAME_MAX);
union {
struct sockaddr_storage ss;
struct sockaddr_in s4;
struct sockaddr_in6 s6;
} client_addr;
struct sockaddr_in server_addr;
const int on = 1, off = 0;
memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));
int res;
SSL *ssl;
BIO *bio;
int sock;
struct timeval timeout;
SSL_CTX *ctx = SSL_CTX_new(DTLS_server_method());
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
if(!SSL_CTX_use_certificate_file(ctx, "/home/matthew/CLionProjects/OpenSSL.Test/cmake-build-debug/certs/cert.crt", SSL_FILETYPE_PEM))
{
perror("cert");
exit(EXIT_FAILURE);
}
if(!SSL_CTX_use_PrivateKey_file(ctx, "/home/matthew/CLionProjects/OpenSSL.Test/cmake-build-debug/certs/key.key", SSL_FILETYPE_PEM))
{
perror("key");
exit(EXIT_FAILURE);
}
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, dtls_verify_callback);
SSL_CTX_set_read_ahead(ctx, 1);
SSL_CTX_set_cookie_generate_cb(ctx, generate_cookie);
SSL_CTX_set_cookie_verify_cb(ctx, &verify_cookie);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(1114);
if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket");
exit(EXIT_FAILURE);
}
if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, (socklen_t) sizeof(on)) < 0)
{
perror("set reuse address");
exit(EXIT_FAILURE);
}
if(setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char*) &on, (socklen_t) sizeof(on)) < 0)
{
perror("set reuse port");
exit(EXIT_FAILURE);
}
if(bind(sock, (const struct sockaddr *) &server_addr, sizeof(server_addr)) < 0)
{
perror("bind");
exit(EXIT_FAILURE);
}
memset(&client_addr, 0, sizeof(struct sockaddr_storage));
/* Create BIO */
bio = BIO_new_dgram(sock, BIO_NOCLOSE);
/* Set and activate timeouts */
timeout.tv_sec = 5;
timeout.tv_usec = 0;
BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout);
ssl = SSL_new(ctx);
SSL_set_bio(ssl, bio, bio);
SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE);
if(!SSL_set_fd(ssl, sock))
{
perror("set fd");
exit(EXIT_FAILURE);
}
res = 0;
while(res <= 0)
{
res = DTLSv1_listen(ssl, (BIO_ADDR *) &client_addr);
if(res < 0)
{
perror("dtls listen"); <--- "Destination address required"
exit(EXIT_FAILURE);
}
}
SSL_accept(ssl);
printf("Hello, World!\n");
return 0;
}
I've added error checks in every nook and cranny I can think of...
What happens is, it loops over DTLSv1_accept() fine until a connection is attempted. As soon as the client tries to connect (sends the Client Hello), DTLSv1_accept() return -1. A call to SSL_get_error gives me 5, which is an SSL_ERROR_SYSCALL.
So I do perror and get Destination address required...
AFAIK the whole point of DTLSv1_listen is to listen for any incoming Client Hellos and populate the BIO_ADDR with the client's address once the handshake is finished...
I am using this to test the server:
openssl s_client -dtls -connect 127.0.0.1:1114
I've spent many hours on this. I am about to give up on OpenSSL and try libressl...
Any help is greatly appreciated.
P.S. the code is complete, in that it should compile and run if you want to try it out.
Thanks!
There are two significant problems in your code sample.
First your code to generate a cookie is not correct. You are supposed to create the
cookie and store it in the location pointed to by cookie, and then fill in the length of the cookie in *cookie_len. So the code should like this:
int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len)
{
memcpy(cookie, "cookie", 6);
*cookie_len = 6;
return 1;
}
However, the most important error comes later when setting up the SSL object for use. OpenSSL has the concept of a BIO for abstracting the underlying transport layer. In order to do DTLS over UDP you need to use a "dgram" BIO. You do this bit correctly:
/* Create BIO */
bio = BIO_new_dgram(sock, BIO_NOCLOSE);
/* Set and activate timeouts */
timeout.tv_sec = 5;
timeout.tv_usec = 0;
BIO_ctrl(bio, BIO_CTRL_DGRAM_SET_RECV_TIMEOUT, 0, &timeout);
ssl = SSL_new(ctx);
SSL_set_bio(ssl, bio, bio);
So at this point the SSL object is correctly setup for the DTLSv1_listen() call. However you then do this:
if(!SSL_set_fd(ssl, sock))
{
perror("set fd");
exit(EXIT_FAILURE);
}
The SSL_set_fd function is an alternative to the SSL_set_bio function. What it does is take the supplied fd, wraps it in a "socket" BIO and then calls SSL_set_bio() with the result. A socket BIO is primarily useful for standard TLS and can't be used for DTLS. So the effect of the above code is to throw away the dgram BIO that you previously set up.
In my tests, if I made the generate_cookie change I suggested above, and removed the SSL_set_fd line, it all started to work as expected.
this maybe is a simple question but I'm trying to just read a server response using the sockets API adapting the code from Geeks for Geeks [site]1, when I try to read the data, it becomes blocked forever in the valread = read(server_fd, buffer, 2048); line, and doesn't execute any of the prints. Am I doing something wrong?
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>
int send_request() {
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[512] = {0};
char *hello = "Hello from server";
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// Forcefully attaching socket to the port 8080
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
// CONNECT TO HOST
struct hostent *he;
char* host = "www.columbia.edu";
he = gethostbyname(host);
if(!he) {
printf("Host doesn't exist!\n");
return 0;
}
address.sin_family = AF_INET;
bcopy(he->h_addr, &address.sin_addr, sizeof(struct in_addr));
address.sin_port = htons(80);
if(connect(server_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_in)) < 0) {
printf("Error in the connection");
return 0;
}
valread = read(server_fd, buffer, 2048);
printf("%s\n", buffer);
printf("%d\n", valread);
printf("%d\n", errno);
return 0;
}
int main(int argc, char const *argv[]) {
send_request();
return 0;
}
You are connecting to an HTTP server. The HTTP protocol specifies that the client (that is, the side that makes the connection to the server) must send a request first. You aren't sending a request, so the server is not going to send you a reply.
Also, this is a bug:
valread = read(server_fd, buffer, 2048);
printf("%s\n", buffer);
The %s format specifier can only be used with C-style strings. It can't be used for arbitrary data. For one thing, how would it know how many bytes to output? The only place that information is currently contained is in valread, and you didn't pass it to printf.
I am new to UPnP development and trying to discover all UPnP device within the local network, and I followed an example from the online resource, but my code will only keep looping at the first response. How could I get another response other than the first one, could I get some hints for this?
Example :
First Response from 192.168.xxx.123, and it will keeps printing the following result:
HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1790
DATE: Thu, 01 Jan 2015 10:43:15 GMT
ST: uuid:4d696xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
USN: uuid:4d696xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
EXT:
SERVER: Linux 2.6 DLNADOC/1.50 UPnP/1.0 ReadyDLNA/1.0.26
LOCATION: http://192.168.xxx.123:xxxx/rootDesc.xml
Content-Length: 0
I checked in Wireshark, and I can see the other device [IP: 192.168.xxx.99] has given me a response, but I am not able to receive it in my code.
I also read a question on SO and used select in my code, but still cannot get it working.
Receiving response(s) from N number of clients in reply to a broadcast request over UDP
The code:
#include <QCoreApplication>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#define RESPONSE_BUFFER_LEN 1024
#define SSDP_MULTICAST "239.255.255.250"
#define SSDP_PORT 1900
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int sock;
size_t ret;
unsigned int socklen;
struct sockaddr_in sockname;
struct sockaddr clientsock;
struct hostent *hostname;
char data[] =
"M-SEARCH * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1900\r\n"
"MAN: \"ssdp:discover\"\r\n"
"ST: ssdp:all\r\n"
"MX: 120\r\n"
"\r\n";
char buffer[RESPONSE_BUFFER_LEN];
unsigned int len = RESPONSE_BUFFER_LEN;
fd_set fds;
struct timeval timeout;
hostname = gethostbyname(SSDP_MULTICAST);
hostname->h_addrtype = AF_INET;
if((sock = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
{
printf("err: socket() failed");
return -1;
}
memset((char *)&sockname, 0, sizeof(struct sockaddr_in));
sockname.sin_family = AF_INET;
sockname.sin_port = htons(SSDP_PORT);
sockname.sin_addr.s_addr = *((unsigned long *)(hostname->h_addr_list[0]));
ret = sendto(sock, data, strlen(data), 0, (struct sockaddr *)&sockname,
sizeof(struct sockaddr_in));
if(ret != strlen(data))
{
printf("err:sendto");
return -1;
}
/* Get response */
FD_ZERO(&fds);
FD_SET(sock, &fds);
timeout.tv_sec = 5;
timeout.tv_usec = 5;
while(select(sock + 1, &fds, NULL, NULL, &timeout) > 0)
{
if(FD_ISSET(sock, &fds))
{
socklen = sizeof(clientsock);
if((len = recvfrom(sock, buffer, len, MSG_PEEK, &clientsock, &socklen)) == (size_t)-1)
{
printf("err: recvfrom");
return -1;
}
buffer[len] = '\0';
/* Check the HTTP response code */
if(strncmp(buffer, "HTTP/1.1 200 OK", 12) != 0)
{
printf("err: ssdp parsing ");
return -1;
}
printf(buffer);
}
else
{
printf("err: no ssdp answer");
}
}
//close(sock);
return a.exec();
}
You are using MSG_PEEK, which means to read the first message in the socket's receive buffer, but not remove it from the buffer.
Therefore every time you call recvfrom you get the first received message.
Change MSG_PEEK to 0 and then each call will read the first message that hasn't been read yet. (So the second call will read the second message, and so on)
I am trying to build a HTTP request using sockets in c.
So, in order to navigate the socket to the correct site ip.
I need to get the site ip.
I have managed to get the host ip but that not always work.
The following code gets the host ip:
host = gethostbyname(host_name);
if (host != NULL) {
memcpy(&inp, host->h_addr_list[0], host->h_length);
sprintf(ip, "%s", inet_ntoa(inp));
}
But that not always work, for example if I want to send the socket to stackoverflow.com and get his HTML content.I used this code and the output was: "198.252.206.16".
And if you enter that ip you can see that it is a wrong ip,so what can I do?
Please help.
P.S that this all my code:
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <errno.h>
#include <assert.h>
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "usage: %s domain_name\nE.g. %s www.yahoo.com/lalal.html\n", argv[0], argv[0]);
return(0);
}
struct protoent *pr;
struct in_addr inp;
int x = 1;
int ret;
char buf[4192];
char ip[16];
struct hostent *host;
int sock, bytes_recieved;
struct sockaddr_in server_addr;
char url[strlen(argv[1])];
strcpy(url,argv[1]);
char *index_page = strstr(argv[1], "/");
char *host_name = strtok(url,"/");
char message[4000];
sprintf(message,"GET %s HTTP/1.1\r\nHost: %s\r\n\r\n",index_page,host_name);
printf("%s",message);
host = gethostbyname(host_name);
if (host != NULL) {
memcpy(&inp, host->h_addr_list[0], host->h_length);
sprintf(ip, "%s", inet_ntoa(inp));
}
else {
printf("ERROR - Host ip was not found.\n\n");
exit(1);
}
printf("%s\n",ip);
pr = getprotobyname("tcp");
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Socket");
exit(1);
}
printf("%s\n",message);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(80);
server_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(server_addr.sin_zero),8);
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
perror("Connect");
exit(1);
}
write(sock, message, strlen(message));
while ((ret = read(sock, buf, 4192)) != 0) {
buf[ret]='\0';
fwrite(buf, ret, sizeof(char), stdout);
x++;
}
if (close(sock) == -1)
printf("Close socket error\n");
return 0;
}
StackExchange hosts multiple sites on the same server. 198.252.206.16 is the IP address of that server, and that is the correct IP address you need to connect your socket to.
When requesting an HTTP resource from a site that resides on a shared server, you must provide an HTTP Host header to specify the site's hostname so the server knows which site you are trying to access.
For example, if you go to http://198.252.206.16, the request would look like this:
(connect to 198.252.206.16)
GET / HTTP 1.1
Host: 198.252.206.16
...
If you go to http://www.stackoverflow.com, the request looks like this:
(connect to 198.252.206.16)
GET / HTTP 1.1
Host: www.stackoverflow.com
...
If you go to http://www.stackexchange.com, the request looks like this:
(connect to 198.252.206.16)
GET / HTTP 1.1
Host: www.stackexchange.com
...
Notice that they all connect to the same IP address.
There is no site associated with the 198.252.206.16 host, which is why you get the error message.
The Host header is required for all HTTP 1.1 requests, and is optional for HTTP 1.0 requests (but an HTTP 1.0 request will fail in this situation if the Host header is missing). It was designed specifically to support multiple sites on a shared server.
I have a C function to check a host and its port, when I use FQDN host name, the function return error like: connect() failed: connect time out, but if I use IP address instead, it seems ok, how to fix this?
Thanks.
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
int is_network_up(char *chkhost, unsigned short chkport) {
int sock;
struct sockaddr_in chksock;
struct hostent *host = NULL;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
syslog(LOG_ERR, "socket() creation error: %s", strerror(errno));
return 0;
}
memset(&chksock, 0, sizeof(chksock));
chksock.sin_family = AF_INET;
chksock.sin_port = htons(chkport);
/* get the server address */
if (inet_pton(AF_INET, chkhost, &(chksock.sin_addr.s_addr)) <= 0) {
if ((host = gethostbyname(chkhost)) == NULL) {
syslog(LOG_ERR, "%s", hstrerror(h_errno));
return 0;
}
memcpy(&(chksock.sin_addr.s_addr), &(host->h_addr_list[0]),
sizeof(struct in_addr));
}
/* try to connect */
if (connect(sock, (struct sockaddr *) &chksock, sizeof(chksock)) < 0) {
syslog(LOG_ERR, "connect() failed: %s", strerror(errno));
return 0;
}
close(sock);
return 1;
}
inet_pton() is the wrong task for that. It only accepts numerical addresses.
In former times, people used to use gethostbyname() for name resolution.
But as we have 2012 meanwhile, this method is outdated for several years now, as it is still restricted to AF_INET.
With the program below, you should achieve about the same and stay future compatible.
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <stdio.h>
int is_network_up(char *chkhost, unsigned short chkport) {
int sock = -1;
struct addrinfo * res, *rp;
int ret = 0;
char sport[10];
snprintf(sport, sizeof sport, "%d", chkport);
struct addrinfo hints = { .ai_socktype=SOCK_STREAM };
if (getaddrinfo(chkhost, sport, &hints, &res)) {
perror("gai");
return 0;
}
for (rp = res; rp && !ret; rp = rp->ai_next) {
sock = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sock == -1) continue;
if (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1) {
char node[200], service[100];
getnameinfo(res->ai_addr, res->ai_addrlen, node, sizeof node, service, sizeof
service, NI_NUMERICHOST);
printf("Success on %s, %s\n", node, service);
ret = 1; /* Success */
}
close(sock);
}
freeaddrinfo(res);
return ret;
}
int main(int argc, char** argv) {
if (argc > 1) {
printf("%s: %d\n", argv[1], is_network_up(argv[1], 22));
}
}
Make sure name resolution is working. See if you can ping the machine by name from the exact same environment in which your code runs.
If ping works, try telnet <machinename> <portnumber> -- If both of those work it is likely a problem with your code (which I did not look at in depth, too sleepy:).
Make sure you're converting anything returned by the OS as an ip address from network order to host order. IIRC, gethostbyname returns binary ip addresses in network order.
ntohl can be used on chksock.sin_addr.s_addr after the memcpy to achieve this.