How to get macro value fom cmake to c++ variable [duplicate] - c++

I'm developing an unique client that has to work on different machines. In every machine the server is running in a different IP address, but this address is known.
I don't want to tell the client which is the IP every time I run it, so I though about tell it in compilation time.
The problem is that when compiling with g++ -DHOSTNAME=127.0.0.1 (also tried with double quotes) the compiler is saying:
error: too many decimal points in number
./include/Client.h:18:25: note: in expansion of macro ‘HOSTNAME’
I tried it using localhost, too.
error: ‘localhost’ was not declared in this scope
./include/Client.h:18:25: note: in expansion of macro ‘HOSTNAME’
Also tried using some things found on the internet.
#define XSTR(x) STR(x)
#define STR(x)
compile error:
./src/BSCClient.cpp:15:45: note: #pragma message: HOSTNAME:
#pragma message("HOSTNAME: " XSTR(HOSTNAME))
./src/BSCClient.cpp:16:39: error: too few arguments to function ‘hostent* gethostbyname(const char*)’
server = gethostbyname(XSTR(HOSTNAME));
At this point I'm thinking that maybe macros isn't the proper way to handle this, but I don't figure out how to do it.
If someone has any reference about it I will be thankful.
EDIT:
These are the codes.
Client.h:
#ifndef __CLIENT_HH__
#define __CLIENT_HH__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string>
#include <iostream>
using namespace std;
#define HOSTNAME 127.0.0.1
#define MAX_MESSAGE_LENGTH 10
class Client {
private:
string client_name;
int sockfd, portno;
struct sockaddr_in serv_addr;
struct hostent *server;
error(const char *msg);
public:
BSCClient (string name, int port);
void identifyme();
void sendData (string data);
string recvData ();
void closeSocket();
};
#endif
Client.cpp
#include "BSCClient.h"
#include <stdlib.h>
#include <time.h>
void BSCClient::error(const char *msg)
{
perror(msg);
exit(0);
}
Client::Client(string name, int port)
{
sockfd = socket(AF_INET, SOCK_STREAM, 0);
portno = port;
client_name = name;
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(HOSTNAME);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR connecting");
sendData(client_name);
}
void Client::identifyme() {
FILE *fp;
fp = popen("id -gn", "r");
char text[6];
fscanf(fp, "%s", text);
pclose(fp);
string data(text);
sendData(data);
}
void Client::sendData (string data) {
const char *sdata = data.c_str();
int n;
n = write(sockfd, sdata, strlen(sdata));
if (n < 0)
error("ERROR writing to socket");
}
string Client::recvData () {
int n;
int bytes;
char *longitud = new char[MAX_MESSAGE_LENGTH+1];
n = read(sockfd, longitud, MAX_MESSAGE_LENGTH);
if (n < 0) {
error("ERROR recieving size of output");
}
bytes=atoi(longitud);
//Para forzar el fin del string (ya que al imprimir el string hay veces que muestra caracteres de más)
longitud[MAX_MESSAGE_LENGTH]='\0';
char *data = new char[bytes];
n = read(sockfd, data, bytes);
if (n < 0)
error("ERROR reading output");
string ret(data);
return ret;
}
void Client::closeSocket() {
close(sockfd);
}

You have to escape the double quotes:
g++ -DHOSTNAME=\"127.0.0.1\"
Otherwise, the quotes are just saying to your shell that 127.0.0.1 is the value you want to give to -DHOSTNAME, which can be useful if the value has whitespaces for example:
g++ -DMAGIC_NUMBER="150 / 5"
(there, MAGIC_NUMBER will be replaced by 150 / 5 without the quotes)
If you want the quotes to be part of the macro (as in #define HOSTNAME "127.0.0.1"), you have to say to your shell that they are part of the value you give to -DHOSTNAME, this is done by escaping them.
EDIT:
Also, as pointed out by Angew, you misused the XSTR trick. It is an other solution to your problem than my answer.
It certainly works like this:
#define XSTR(x) STR(x)
#define STR(x) #x
With that you don't have to escape the quotes.
These two macros change the text 127.0.0.1 into "127.0.0.1". The XSTR macro allows HOSTNAME to be expanded to 127.0.0.1 before the STR macro converts it to "127.0.0.1". If you used directly the STR macro, you would end up with "HOSTNAME" instead of "127.0.0.1".
I think I prefer the escaping solution to the use of a trick involving two macros in the code, but that works too.

It seems odd that you'd want to hard-code this into the executable. It should be more flexible to use something like getenv("MY_SERVER_ADDR") and just set that environment variable before running your server. Or of course you could do the more typical thing and take it as a command line argument, but something tells me you already decided not to do that.
A slightly weirder idea if you are on Linux is to write the IP address into a text file and create an ELF object file from that using ld and objcopy; you can then load this into your app as a shared object or even a static one if you really want to "hard code" it. But I'm not sure why this would be preferable to the previously mentioned options.

Related

Problem with socket code C++ when send and hex message

I try to send hex data to another device. The compilation does not lead to errors. But when I launch the function from the terminal nothing happens but a light should turn on. I put a cout on connect () to see if I have some connection problems but it answers 0 (so I have no problems) maybe it's how I write the hex code?
Besides the code I also insert the command line that I execute with the Hex code. The strange thing is that if I use any program like PacketSender with the same parameters it works.
./main 192.168.2.170 26810000A7
in attachment I also add the screenshot made on PacketSender.In addition what I can say is that if I leave the recv () command in the code once I give the command from the terminal the prompt remains pending ... if instead I comment the recv () once I give the command from the prompt it returns to the prompt but without doing anything (so without turning on the light). Does anyone have any ideas?
enter image description here
#include <stdio.h>
#include <errno.h>
#include <string>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <unistd.h>
#include <iostream>
#include <unistd.h>
using namespace std;
// per compilare gcc connect_PE.cpp -lstdc++ -o main
int main(int argc, char *argv[])
{
int sockfd, n;
struct sockaddr_in servaddr;
std::string serveraddr = argv[1];
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(serveraddr.c_str());
servaddr.sin_port = htons(9761);
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
cout << connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
const std::string hex = argv[2];
char *number = strcpy(new char[hex.length() + 1], hex.c_str());
send(sockfd, &number, 8, 0);
recv(sockfd, &number, 8, 0);
}
You’re passing the address of number when you should be passing its value.
Or even better, just pass hex.data() or &hex[0] – there’s no point in copying it.
Also note that you are sending a string of characters, not numbers in hexadecimal form.
(The first char is not 0x26 - that is, 38 - but ’2’, wich is probably 50.)
If you want to send the corresponding numbers, you need to convert the input first.
Sockets work very poorly with everything that is not char*, so it is better to immediately send a string from the terminal argument, and perform all the necessary transformations on the server.

Raspbian C++ Error: expected primary expression before 'struct'

I am trying to write a program for my raspberry pi that changes its system time to the time from a GPS unit on the same network. The GPS sends out a 72 byte UDP packet across port 3000. I am new to socket programming so I am unsure where I am going wrong.
The trouble that I am having is that I can't seem to get it to build with g++. I am getting the following error:
So the main error seems to be in the line
char A = struct sockaddr_in address;
Here is the start of my program and the method where I create the socket and where the error is located, if you would like the main method of my program then I will add it too.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <math.h>
// defines the socket used by the GPS
#define PORT 3000
/****************************/
int CreateSocket(int port)
/****************************/
{
// Create an UDP-socket
int sock = socket(AF_INET, SOCK_DGRAM, 0);
// Check if UDP-socket was created
if(sock==-1)
{
fprintf(stderr, "1CreateSocket: socket failed\n");
return -1;
}
// Bind it to the local IP-address
struct sockaddr_in address;
char A = struct sockaddr_in address;
fprintf(stderr, A);
// Pointer to the block of memory to fill with address data
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET; // Address family for IP-address
address.sin_addr.s_addr = htonl(INADDR_ANY); // converts the unsigned integer hostlong from host byte order to network byte order
address.sin_port = htons(port); // converts the unsigned short integer hostshort from host byte order to network byte order
// Check if IP-address is correct, if not Socket failed. Otherwise it returns the socket
if(bind(sock, (struct sockaddr *) &address, sizeof(address))==-1)
{
fprintf(stderr, "2CreateSocket: bind failed\n");
close(sock);
return -1;
}
return sock;
}
Can anyone see any obvious errors here? Thanks
You don't really need these two lines:
char A = struct sockaddr_in address;
fprintf(stderr, A);
You can delete them, since they don't do anything useful, and they have a syntax error.
And to do some extra cleanup, the comment of the binding above those lines that can be deleted should actually go above the call to bind().

POP3 server - basic client operations in raw C++

I've spent at least 2 last hours searching for a way to make a simple connection to a POP3 server and get the number of messages waiting on it. As it's childlish-easy in C# and seems pretty basic in C++ on linux, I just can't find even the slightest tutorial on how to make it work on Windows.
I don't want to use any third-party libraries - i just want to code a simple console program, using raw C++ only, just to do some basic stuff as described above. All the sources I've tried to study are like:
POP3 is a protocol that has somethng to do with emails and it's very simple. Now let's proceed to writing a multi-platform POP3 server-client application, using a F16 fighter jet and inventing a time machine in progress.
I just can't seem to find any SIMPLE solutions...
I've written (with some help) a simple snippet that SHOULD work on linux - at least according to the tutorials; I have no means to check it right now.
Hovewer, the C++ is not my "native language" and when I try to transfer it into Windows, I just fall from one hole into the other and have to spend yet another quarter of an hour GGoogle'ing the solution.
At this point, the code is compiling, but the linker fails. It's strange, because I've added the ws2_32.lib to the linker, so it SHOULD work just fine. In return, I only get loads of LNK2019.
Can you please help me with the code or provide any link to a SIMPLE solution that works on Windows?
The code:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN 1
#include <winsock2.h>
#include <windows.h>
#else
#endif
#ifndef in_addr_t
#define in_addr_t long
#endif
#include <string.h>
void err(char *where) {
fprintf(stderr, "error in %s: %d\n", where, errno);
exit(1);
}
int main(int argc, char *argv[]) {
char *remote = "some_address";
struct servent *sent;
struct protoent *pent;
int port;
int sock;
int result;
in_addr_t ipadr;
struct sockaddr_in addr;
struct hostent *hent;
char buf[2048];
sent = getservbyname("http", "pop3");
if(sent == NULL)
err("getservbyname");
port = sent->s_port;
pent = getprotobyname("pop3");
if(pent == NULL)
err("getprotobyname");
hent = gethostbyname(remote);
printf("Host: %s\n", hent->h_name);
printf("IP: %s\n", inet_ntoa(*((struct in_addr *)hent->h_addr)));
addr.sin_family = AF_INET;
addr.sin_port = port;
addr.sin_addr = *((struct in_addr *)hent->h_addr);
memset(addr.sin_zero, '\0', 8);
sock = socket(AF_INET, SOCK_STREAM, pent->p_proto);
if(sock < 0)
err("socket");
result = connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr));
if(result < 0)
err("connect");
}
You have to add WSAStartup before you use any Winsock function. When you are done, you have to call WSACleanup.
Example(from msdn):
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return 1;
}
//Do stuf here
WSACleanup();

how to scan wireless network and display the list of all computers and devices connected

I need to build a tool (c++) very much like "Wireless Network Watcher" which is a small utility that scans your wireless network and displays the list of all computers and devices that are currently connected to your network.
here's the existing tool http://www.nirsoft.net/utils/wireless_network_watcher.html
I need to know what are the win32 sdk functions to use to build this kind of functionality: scan the wireless network I am connected to and display all computers and devices connected to it.
ok, it seems is done this way: first sent an ARP request packet to each possible IP address in the network (you calculate them based on the net mask and the interface ip), for this step you can use SendARP functions. Then you have to call getnameinfo for each IP that responded previously, or you can send an NetBios request packet (port 137) to retreive the name of the device, if it has one, or know how to respond to that request.
for some networks this can take awhile (very long time).
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/ip_icmp.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
// Define the Packet Constants
// ping packet size
#define PING_PKT_S 64
// Automatic port number
#define PORT_NO 0
// Automatic port number
#define PING_SLEEP_RATE 1000000
// Gives the timeout delay for receiving packets
// in seconds
#define RECV_TIMEOUT 1
// Performs a DNS lookup
char* dns_lookup(char* addr_host, struct sockaddr_in* addr_con) {
// printf("\nResolving DNS..\n");
struct hostent* host_entity;
char* ip = (char*)malloc(NI_MAXHOST * sizeof(char));
int i;
if ((host_entity = gethostbyname(addr_host)) == NULL) {
// No ip found for hostname
return NULL;
}
// filling up address structure
strcpy(ip, inet_ntoa(*(struct in_addr*)host_entity->h_addr));
(*addr_con).sin_family = host_entity->h_addrtype;
(*addr_con).sin_port = htons(PORT_NO);
(*addr_con).sin_addr.s_addr = *(long*)host_entity->h_addr;
return ip;
}
// Resolves the reverse lookup of the hostname
char* reverse_dns_lookup(char* ip_addr) {
struct sockaddr_in temp_addr;
socklen_t len;
char buf[NI_MAXHOST], *ret_buf;
temp_addr.sin_family = AF_INET;
temp_addr.sin_addr.s_addr = inet_addr(ip_addr);
len = sizeof(struct sockaddr_in);
if (getnameinfo((struct sockaddr*)&temp_addr, len, buf, sizeof(buf), NULL, 0,
NI_NAMEREQD)) {
// printf("Could not resolve reverse lookup of hostname\n");
return NULL;
}
ret_buf = (char*)malloc((strlen(buf) + 1) * sizeof(char));
strcpy(ret_buf, buf);
return ret_buf;
}
// Driver Code
int main(int argc, char* argv[]) {
int sockfd;
char *ip_addr, *reverse_hostname;
struct sockaddr_in addr_con;
int addrlen = sizeof(addr_con);
char net_buf[NI_MAXHOST];
int i = 0;
for (int i = 1; i < 255; ++i) {
char ip[80];
sprintf(ip, "192.168.2.%d", i);
ip_addr = dns_lookup(ip, &addr_con);
if (ip_addr == NULL) {
// printf("\nDNS lookup failed! Could not resolve hostname!\n");
continue;
}
reverse_hostname = reverse_dns_lookup(ip_addr);
if (reverse_hostname == NULL) {
// printf("\nDNS lookup failed! Could not resolve hostname!\n");
continue;
}
// printf("\nTrying to connect to '%s' IP: %s\n",ip, ip_addr);
printf("\nReverse Lookup domain: %s", reverse_hostname);
printf("\n %s \n", ip);
}
return 0;
}
result:
Reverse Lookup domain: router.asus.com
192.168.2.1
Reverse Lookup domain: DESKTOP-CMK0J2S
192.168.2.10
Reverse Lookup domain: User255
192.168.2.14
Very vague question, there is no single "find all devices" feature to Windows, wireless or even networking in general. You need to scan fer certain services like netbios (139), UPNP, etc. Also, none of this is specific to wireless conenctions.

Send and Receive a file in socket programming in Linux with C/C++ (GCC/G++)

I would like to implement a client-server architecture running on Linux using sockets and C/C++ language that is capable of sending and receiving files. Is there any library that makes this task easy? Could anyone please provide an example?
The most portable solution is just to read the file in chunks, and then write the data out to the socket, in a loop (and likewise, the other way around when receiving the file). You allocate a buffer, read into that buffer, and write from that buffer into your socket (you could also use send and recv, which are socket-specific ways of writing and reading data). The outline would look something like this:
while (1) {
// Read data into buffer. We may not have enough to fill up buffer, so we
// store how many bytes were actually read in bytes_read.
int bytes_read = read(input_file, buffer, sizeof(buffer));
if (bytes_read == 0) // We're done reading from the file
break;
if (bytes_read < 0) {
// handle errors
}
// You need a loop for the write, because not all of the data may be written
// in one call; write will return how many bytes were written. p keeps
// track of where in the buffer we are, while we decrement bytes_read
// to keep track of how many bytes are left to write.
void *p = buffer;
while (bytes_read > 0) {
int bytes_written = write(output_socket, p, bytes_read);
if (bytes_written <= 0) {
// handle errors
}
bytes_read -= bytes_written;
p += bytes_written;
}
}
Make sure to read the documentation for read and write carefully, especially when handling errors. Some of the error codes mean that you should just try again, for instance just looping again with a continue statement, while others mean something is broken and you need to stop.
For sending the file to a socket, there is a system call, sendfile that does just what you want. It tells the kernel to send a file from one file descriptor to another, and then the kernel can take care of the rest. There is a caveat that the source file descriptor must support mmap (as in, be an actual file, not a socket), and the destination must be a socket (so you can't use it to copy files, or send data directly from one socket to another); it is designed to support the usage you describe, of sending a file to a socket. It doesn't help with receiving the file, however; you would need to do the loop yourself for that. I cannot tell you why there is a sendfile call but no analogous recvfile.
Beware that sendfile is Linux specific; it is not portable to other systems. Other systems frequently have their own version of sendfile, but the exact interface may vary (FreeBSD, Mac OS X, Solaris).
In Linux 2.6.17, the splice system call was introduced, and as of 2.6.23 is used internally to implement sendfile. splice is a more general purpose API than sendfile. For a good description of splice and tee, see the rather good explanation from Linus himself. He points out how using splice is basically just like the loop above, using read and write, except that the buffer is in the kernel, so the data doesn't have to transferred between the kernel and user space, or may not even ever pass through the CPU (known as "zero-copy I/O").
Do aman 2 sendfile. You only need to open the source file on the client and destination file on the server, then call sendfile and the kernel will chop and move the data.
Minimal runnable POSIX read + write example
Usage:
get two computers on a LAN.
For example, this will work if both computers are connected to your home router in most cases, which is how I tested it.
On the server computer:
Find the server local IP with ifconfig, e.g. 192.168.0.10
Run:
./server output.tmp 12345
On the client computer:
printf 'ab\ncd\n' > input.tmp
./client input.tmp 192.168.0.10 12345
Outcome: a file output.tmp is created on the sever computer containing 'ab\ncd\n'!
server.c
/*
Receive a file over a socket.
Saves it to output.tmp by default.
Interface:
./executable [<output_file> [<port>]]
Defaults:
- output_file: output.tmp
- port: 12345
*/
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *file_path = "output.tmp";
char buffer[BUFSIZ];
char protoname[] = "tcp";
int client_sockfd;
int enable = 1;
int filefd;
int i;
int server_sockfd;
socklen_t client_len;
ssize_t read_return;
struct protoent *protoent;
struct sockaddr_in client_address, server_address;
unsigned short server_port = 12345u;
if (argc > 1) {
file_path = argv[1];
if (argc > 2) {
server_port = strtol(argv[2], NULL, 10);
}
}
/* Create a socket and listen to it.. */
protoent = getprotobyname(protoname);
if (protoent == NULL) {
perror("getprotobyname");
exit(EXIT_FAILURE);
}
server_sockfd = socket(
AF_INET,
SOCK_STREAM,
protoent->p_proto
);
if (server_sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
if (setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
perror("setsockopt(SO_REUSEADDR) failed");
exit(EXIT_FAILURE);
}
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(server_port);
if (bind(
server_sockfd,
(struct sockaddr*)&server_address,
sizeof(server_address)
) == -1
) {
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(server_sockfd, 5) == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
fprintf(stderr, "listening on port %d\n", server_port);
while (1) {
client_len = sizeof(client_address);
puts("waiting for client");
client_sockfd = accept(
server_sockfd,
(struct sockaddr*)&client_address,
&client_len
);
filefd = open(file_path,
O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR);
if (filefd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
do {
read_return = read(client_sockfd, buffer, BUFSIZ);
if (read_return == -1) {
perror("read");
exit(EXIT_FAILURE);
}
if (write(filefd, buffer, read_return) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
} while (read_return > 0);
close(filefd);
close(client_sockfd);
}
return EXIT_SUCCESS;
}
client.c
/*
Send a file over a socket.
Interface:
./executable [<input_path> [<sever_hostname> [<port>]]]
Defaults:
- input_path: input.tmp
- server_hostname: 127.0.0.1
- port: 12345
*/
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
int main(int argc, char **argv) {
char protoname[] = "tcp";
struct protoent *protoent;
char *file_path = "input.tmp";
char *server_hostname = "127.0.0.1";
char *server_reply = NULL;
char *user_input = NULL;
char buffer[BUFSIZ];
in_addr_t in_addr;
in_addr_t server_addr;
int filefd;
int sockfd;
ssize_t i;
ssize_t read_return;
struct hostent *hostent;
struct sockaddr_in sockaddr_in;
unsigned short server_port = 12345;
if (argc > 1) {
file_path = argv[1];
if (argc > 2) {
server_hostname = argv[2];
if (argc > 3) {
server_port = strtol(argv[3], NULL, 10);
}
}
}
filefd = open(file_path, O_RDONLY);
if (filefd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
/* Get socket. */
protoent = getprotobyname(protoname);
if (protoent == NULL) {
perror("getprotobyname");
exit(EXIT_FAILURE);
}
sockfd = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
/* Prepare sockaddr_in. */
hostent = gethostbyname(server_hostname);
if (hostent == NULL) {
fprintf(stderr, "error: gethostbyname(\"%s\")\n", server_hostname);
exit(EXIT_FAILURE);
}
in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
if (in_addr == (in_addr_t)-1) {
fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));
exit(EXIT_FAILURE);
}
sockaddr_in.sin_addr.s_addr = in_addr;
sockaddr_in.sin_family = AF_INET;
sockaddr_in.sin_port = htons(server_port);
/* Do the actual connection. */
if (connect(sockfd, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {
perror("connect");
return EXIT_FAILURE;
}
while (1) {
read_return = read(filefd, buffer, BUFSIZ);
if (read_return == 0)
break;
if (read_return == -1) {
perror("read");
exit(EXIT_FAILURE);
}
/* TODO use write loop: https://stackoverflow.com/questions/24259640/writing-a-full-buffer-using-write-system-call */
if (write(sockfd, buffer, read_return) == -1) {
perror("write");
exit(EXIT_FAILURE);
}
}
free(user_input);
free(server_reply);
close(filefd);
exit(EXIT_SUCCESS);
}
GitHub upstream.
Further comments
Possible improvements:
Currently output.tmp gets overwritten each time a send is done.
This begs for the creation of a simple protocol that allows to pass a filename so that multiple files can be uploaded, e.g.: filename up to the first newline character, max filename 256 chars, and the rest until socket closure are the contents. Of course, that would require sanitation to avoid a path transversal vulnerability.
Alternatively, we could make a server that hashes the files to find filenames, and keeps a map from original paths to hashes on disk (on a database).
Only one client can connect at a time.
This is specially harmful if there are slow clients whose connections last for a long time: the slow connection halts everyone down.
One way to work around that is to fork a process / thread for each accept, start listening again immediately, and use file lock synchronization on the files.
Add timeouts, and close clients if they take too long. Or else it would be easy to do a DoS.
poll or select are some options: How to implement a timeout in read function call?
A simple HTTP wget implementation is shown at: How to make an HTTP get request in C without libcurl?
Tested on Ubuntu 15.10.
This file will serve you as a good sendfile example : http://tldp.org/LDP/LGNET/91/misc/tranter/server.c.txt