Sending mail to smtp server requiring authentication using Dev C++ - c++

I am working on a program that connects to a tcp server, get some data and under certain conditions need to send an alert via email.(I'm helping someone with a school project)
I'm using DevC++.
It's been a few years since I've had anything to do with programming and have never done any programming in a network environment. (Hope that make sense)
I got the TCP client and log file part going, but I can't get the mail sending part to work.
Since I'm relatively inexperienced I've already wasted a lot of time, for example first of all I thought of trying POCO, but now it looks like you need Visual C++ to build the libraries.
Next I tried jwsmtp, but the examples I could find didn't do authentication, and it seems that authentication is a must nowadays. Next I tried libCurl, but can't get the examples to work, first of all I get CURLOPT_MAIL_FROM was not declared in this scope, I read in some post that is caused by error in newest version, then the curl header files started giving all sorts of errors.
My problem is that I am now fast running out of time. I would love to get it all working by myself, and even learn enough to write my own code, not just modifying and pasting together examples, but I made a promise and the deadline doesn't give me that option.
Can someone please help with anything that will actually work on windows using DevC++ sending mail to a gmail account?

I assume you will be using Mingw and MSYS.. What I did was download the latest OpenSSL from: http://www.openssl.org/source/
Then I opened MSYS and ran the following commands:
./configure mingw no-shared --prefix='C:/OpenSSL'
It should print Configured for mingw if successful.
Next I went to the OpenSSL source and then the test folder. I opened md2test.c and replaced dummytest.c with: #include "dummytest.c". I did the same thing for rc5test.c and for jpaketest.c.
Next I ran the following commands:
make depend && make install
This will build the static libraries. If you want to build the shared libraries then you need to replace no-shared with shared in the first command (the ./configure line).
When finished, I created some raw sockets and sent emails as follows (need to do some error checking within the sendemail function but I hope you get the gist of it.. It works):
/** © 2014, Brandon T. All Rights Reserved.
*
* This file is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this file. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined _WIN32 || defined _WIN64
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#endif
#include <stdio.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#ifndef __cplusplus
typedef enum {false, true} bool;
#endif
bool ssl_init_count = 0;
typedef struct
{
int sock;
const char* address;
unsigned short port;
} sock_info;
typedef struct
{
SSL* ssl;
SSL_CTX* ctx;
} ssl_info;
typedef struct
{
char b64username[256];
char b64password[256];
} email;
bool initsocket()
{
#if defined _WIN32 || defined _WIN64
WSADATA wsaData = {0};
return !WSAStartup(MAKEWORD(2, 2), &wsaData);
#else
return true;
#endif
}
void destroysocket(sock_info* info)
{
#if defined _WIN32 || defined _WIN64
shutdown(info->sock, SD_BOTH);
closesocket(info->sock);
WSACleanup();
#else
shutdown(info->sock, SHUT_RDWR);
close(info->sock);
#endif
}
bool connectsocket(sock_info* info)
{
struct sockaddr_in* sockaddr_ipv4 = NULL;
struct addrinfo* it = NULL, *result = NULL;
getaddrinfo(info->address, NULL, NULL, &result);
for (it = result; it != NULL; it = it->ai_next)
{
sockaddr_ipv4 = (struct sockaddr_in*)it->ai_addr;
info->address = inet_ntoa(sockaddr_ipv4->sin_addr);
if (strncmp(info->address, "0.0.0.0", 7))
break;
}
freeaddrinfo(result);
if ((info->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
perror("Error creating socket..");
return false;
}
struct sockaddr_in SockAddr;
memset(&SockAddr, 0, sizeof(SockAddr));
SockAddr.sin_port = htons(info->port);
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = inet_addr(info->address);
if (connect(info->sock, (struct sockaddr*)&SockAddr, sizeof(SockAddr)) < 0)
{
perror("Error connecting socket..");
return false;
}
return true;
}
bool setssl(sock_info* sockinfo, ssl_info* sslinfo)
{
sslinfo->ctx = SSL_CTX_new(SSLv23_client_method());
if (sslinfo->ctx)
{
sslinfo->ssl = SSL_new(sslinfo->ctx);
SSL_set_fd(sslinfo->ssl, sockinfo->sock);
return SSL_connect(sslinfo->ssl) != -1;
}
return false;
}
void removessl(sock_info* sockinfo, ssl_info* sslinfo)
{
if (sslinfo->ctx)
{
SSL_CTX_free(sslinfo->ctx);
}
if (sslinfo->ssl)
{
SSL_shutdown(sslinfo->ssl);
SSL_free(sslinfo->ssl);
}
sslinfo->ssl = NULL;
sslinfo->ctx = NULL;
}
void initssl()
{
if (!ssl_init_count)
{
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
}
++ssl_init_count;
}
void freessl()
{
if (!--ssl_init_count)
{
ERR_free_strings();
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
}
}
void sslb64encode(const char* buffer, char* outbuffer)
{
char* b64str = NULL;
BIO* b64 = BIO_new(BIO_f_base64());
BIO* mem = BIO_new(BIO_s_mem());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
b64 = BIO_push(b64, mem);
BIO_write(b64, buffer, strlen(buffer));
BIO_flush(b64);
int len = BIO_get_mem_data(mem, &b64str);
memcpy(outbuffer, b64str, len);
outbuffer[len] = '\0';
BIO_free_all(b64);
}
void initemail(const char* username, const char* password, email* outemail)
{
char ubuffer[256];
char pbuffer[256];
unsigned int bytes_written = 0;
sslb64encode(username, &ubuffer[0]);
sslb64encode(password, &pbuffer[0]);
sprintf(outemail->b64username, "%s", ubuffer);
sprintf(outemail->b64password, "%s", pbuffer);
}
bool printsocketbuffer(ssl_info* sslinfo)
{
char buffer[1024];
unsigned int bytes_read = SSL_read(sslinfo->ssl, &buffer[0], sizeof(buffer));
if (bytes_read > 0)
{
printf("%.*s", bytes_read, buffer);
return true;
}
return false;
}
void sendemail(ssl_info* sslinfo, const char* username, const char* password, const char* recipient, const char* from, const char* to, const char* message, const char* subject, unsigned int messagelen)
{
email em;
char buffer[512];
unsigned int bufflen = sizeof(buffer);
initemail(username, password, &em);
SSL_write(sslinfo->ssl, "HELO\r\n", 6);
printsocketbuffer(sslinfo);
SSL_write(sslinfo->ssl, "AUTH LOGIN\r\n", 12);
printsocketbuffer(sslinfo);
bufflen = sprintf(buffer, "%s\r\n", em.b64username);
SSL_write(sslinfo->ssl, buffer, bufflen);
printsocketbuffer(sslinfo);
bufflen = sprintf(buffer, "%s\r\n", em.b64password);
SSL_write(sslinfo->ssl, buffer, bufflen);
printsocketbuffer(sslinfo);
printsocketbuffer(sslinfo);
bufflen = sprintf(buffer, "MAIL FROM: <%s>\r\n", username);
SSL_write(sslinfo->ssl, buffer, bufflen);
printsocketbuffer(sslinfo);
bufflen = sprintf(buffer, "RCPT TO: <%s>\r\n", recipient);
SSL_write(sslinfo->ssl, buffer, bufflen);
printsocketbuffer(sslinfo);
SSL_write(sslinfo->ssl, "DATA\r\n", 6);
printsocketbuffer(sslinfo);
bufflen = sprintf(buffer, "From: <%s><%s>\r\n", from, username);
bufflen += sprintf(&buffer[bufflen], "To: <%s><%s>\r\n", to, recipient);
bufflen += sprintf(&buffer[bufflen], "Subject: <%s>\r\n", subject);
SSL_write(sslinfo->ssl, buffer, bufflen);
bufflen = 0;
while (bufflen < messagelen)
{
bufflen += SSL_write(sslinfo->ssl, &message[bufflen], messagelen - bufflen);
}
SSL_write(sslinfo->ssl, "\r\n.\r\n", 5);
printsocketbuffer(sslinfo);
SSL_write(sslinfo->ssl, "QUIT\r\n", 6);
printsocketbuffer(sslinfo);
}
int main()
{
ssl_info sslinfo = {0};
sock_info sockinfo = {0, "smtp.gmail.com", 465};
const char* username = "ICantChooseUsernames#gmail.com";
const char* password = "*****";
const char* recipient = "ICantChooseUsernames#gmail.com";
const char* message = "hello there!";
const char* subject = "Testing Emails";
const char* from = "Brandon";
const char* to = "Brandon";
unsigned int messagelen = strlen(message);
if (initsocket())
{
if (connectsocket(&sockinfo))
{
initssl();
if (setssl(&sockinfo, &sslinfo))
{
sendemail(&sslinfo, username, password, recipient, from, to, message, subject, messagelen);
}
else
{
ERR_print_errors_fp(stderr);
}
removessl(&sockinfo, &sslinfo);
freessl();
}
destroysocket(&sockinfo);
}
return 0;
}

Well it seems I'm not going to get this working. Tried all openssl, mingw and msys directories. After speaking to a friend I got Visual Studio Express and got a c# example to work fairly quickly. Not having even seen c# before I couldn't rewrite everything else, so I just modified my example to make a mail sending application I call from my other program. Maybe not the best way but hey, it works and my time is up.
Hopefully I will be able to start learning this stuff.
Thanks for trying to help this ignoramus, I really appreciate it.

Related

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

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.

iOS sockets IPv6 Support

I am getting an error when I try to connect to my ipv4 server. Currently the ios app users are required to enter their sever's an IP address, port, and account information.
The ios app then calls Connect on the SocketSender class (included in the header search path) which in turns calls the connect function of Socket.h and then checks the results.
Connect - SocketSender.cpp
bool SocketSender::Connect (const char *host, int port, CApiError &err)
{
errno = 0;
struct hostent *hostinfo;
hostinfo = gethostbyname (host);
if (!hostinfo) {
#ifdef PLATFORM_WIN32
m_nLastErrorNo = SOCKET_ERRNO();
err.SetSystemError(m_nLastErrorNo);
#else
/* Linux stores the gethostbyname error in h_errno. */
m_nLastErrorNo = EINVAL; // h_errno value is incompatible with the "normal" error codes
err.SetError(FIX_SN(h_errno, hstrerror(h_errno)), CATEGORY_SYSTEM | ERR_TYPE_ERROR);
#endif
return false;
}
socket_fd = socket (AF_INET, SOCK_STREAM, 0);
if (socket_fd == -1) {
m_nLastErrorNo = SOCKET_ERRNO();
err.SetSystemError(m_nLastErrorNo);
return false;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons (port);
address.sin_addr = *(struct in_addr *) *hostinfo->h_addr_list;
int result;
SetSocketOptions();
result = connect (socket_fd, (struct sockaddr *) &address, sizeof (address));
if (result == -1) {
if (IS_IN_PROGRESS()) {
fd_set f1,f2,f3;
struct timeval tv;
/* configure the sets */
FD_ZERO(&f1);
FD_ZERO(&f2);
FD_ZERO(&f3);
FD_SET(socket_fd, &f2);
FD_SET(socket_fd, &f3);
/* we will have a timeout period */
tv.tv_sec = 5;
tv.tv_usec = 0;
int selrez = select(socket_fd + 1,&f1,&f2,&f3,&tv);
if (selrez == -1) { // socket error
m_nLastErrorNo = SOCKET_ERRNO();
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
if (FD_ISSET(socket_fd, &f3)) { // failed to connect ..
int sockerr = 0;
#ifdef PLATFORM_WIN32
int sockerr_len = sizeof(sockerr);
#else
socklen_t sockerr_len = sizeof(sockerr);
#endif
getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&sockerr, &sockerr_len);
if (sockerr != 0) {
m_nLastErrorNo = sockerr;
} else {
#ifdef PLATFORM_WIN32
m_nLastErrorNo = ERROR_TIMEOUT; // windows actually does not specify the error .. is this ok?
#else
m_nLastErrorNo = ETIMEDOUT;
#endif
}
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
if (!FD_ISSET(socket_fd, &f2)) { // cannot read, so some (unknown) error occured (probably time-out)
int sockerr = 0;
#ifdef PLATFORM_WIN32
int sockerr_len = sizeof(sockerr);
#else
socklen_t sockerr_len = sizeof(sockerr);
#endif
getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&sockerr, &sockerr_len);
if (sockerr != 0) {
m_nLastErrorNo = sockerr;
} else {
#ifdef PLATFORM_WIN32
m_nLastErrorNo = ERROR_TIMEOUT; // windows actually does not specify the error .. is this ok?
#else
m_nLastErrorNo = ETIMEDOUT;
#endif
}
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
#ifndef PLATFORM_WIN32 // FIXME: is the same needed for windows ?
// unix always marks socket as "success", however error code has to be double-checked
int error = 0;
socklen_t len = sizeof(error);
if (getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
err.SetSystemError();
return false;
}
if(error != 0) {
m_nLastErrorNo = error;
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
#endif
} else {
m_nLastErrorNo = SOCKET_ERRNO();
Disconnect(true);
err.SetSystemError(m_nLastErrorNo);
return false;
}
}
m_nIP = ntohl(address.sin_addr.s_addr);
m_bServerSocket = false;
return true;
}
That is the original version that worked without any problems. When i changed the above to use AF_INET6 and in_addr6->sin6_addr, i kept getting errors and the application failed to connect. I tried using getaddrinfo but this still did not connect.
struct addrinfo hints, *res, *res0;
int error;
const char *cause = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT;
error = getaddrinfo(host, "PORT", &hints, &res0);
if (error) {
errx(1, "%s", gai_strerror(error));
/*NOTREACHED*/
}
socket_fd = -1;
printf("IP addresses for %s:\n\n", host);
int result;
void *addr;
char *ipver;
for (res = res0; res!=NULL; res = res->ai_next) {
socket_fd = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (socket_fd < 0) {
cause = "socket";
continue;
}
if ((result = connect(socket_fd, res->ai_addr, res->ai_addrlen)) < 0) {
cause = "connect";
close(socket_fd);
socket_fd = -1;
continue;
}
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (res->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
SetSocketOptions();
break; /* okay we got one */
}
I need to make it backwards compatible with ipv6 and ipv4. Any help would be much appreciated as i have been stuck testing this for the past week. Also if anyone knows how to debug the SocketSender.cpp on XCode that would be alot of help.
So after two weeks of testing out different approaches and familiarizing myself with networking (POSIX) I finally got this to work mainly due to #user102008 suggestion.
This is relevant to Client-Server applications.My application is a client application that connects to a IPv4 server/system at a remote location. We have yet to support IPv6 for our products which include clients(iOS,android,windows,unix) and servers (windows & unix), but will support upon future releases. The reason for this support was solely due to Apple changing their apple review process environment.
Approach, Tips and Issues
Apple has provided a way to test IPv6 compatibility with your app. This is sharing your connection from your Ethernet using NAT64/DNS64. This failed many times for me. After researching and resetting my SMC, I came across this article and realized i may have been messing with the configuration too much. So I reset my SMC, restarted and created the internet sharing host. Always remember to turn off WiFi before making any changes to internet sharing.
Users were required to connect to the server with a IPv4 IP address. The Application ran perfectly on an IPv4 networking but failed in an IPv6 network. This was due to the app not resolving the IP address literal. The networking library my application uses was a cpp library that was included as a preprocess macro. One of the biggest annoyance was trying to debug, because you cant debug compile time code. So what I did was move over my cpp files with their headers to the project (luckily it was only 3 files).
IMPORTANT TO AYONE PASSING PORT NUMBERS. This is tied to #2 and resolving the IPv4 literal. I used apples exact implementation on the networking overview (listing 10-1). Every time i tested, the connect function was returning -1, meaning it did not connect. Thanks to #user102008 providing me with this article, I realized apple implementation of getaddrinfo was broken when trying to pass a string literal for the port. Yes, they ask for a constant char, even when trying c_str() it would still return port number of 0. For this reason, an apple developer who Ive noticed answer and addresses countless networking problems provided a work around. This fixed my issue of the port continuously returning 0, code is posted below as well. What i did, was simply add this into my networking class (SocketSender.cpp) and instead of calling getaddrinfo in Connect, i called get getaddrinfo_compat. This allowed me to connect perfectly in IPv4 and IPv6 networks.
static int getaddrinfo_compat(
const char * hostname,
const char * servname,
const struct addrinfo * hints,
struct addrinfo ** res
) {
int err;
int numericPort;
// If we're given a service name and it's a numeric string, set `numericPort` to that,
// otherwise it ends up as 0.
numericPort = servname != NULL ? atoi(servname) : 0;
// Call `getaddrinfo` with our input parameters.
err = getaddrinfo(hostname, servname, hints, res);
// Post-process the results of `getaddrinfo` to work around <rdar://problem/26365575>.
if ( (err == 0) && (numericPort != 0) ) {
for (const struct addrinfo * addr = *res; addr != NULL; addr = addr->ai_next) {
in_port_t * portPtr;
switch (addr->ai_family) {
case AF_INET: {
portPtr = &((struct sockaddr_in *) addr->ai_addr)->sin_port;
} break;
case AF_INET6: {
portPtr = &((struct sockaddr_in6 *) addr->ai_addr)->sin6_port;
} break;
default: {
portPtr = NULL;
} break;
}
if ( (portPtr != NULL) && (*portPtr == 0) ) {
*portPtr = htons(numericPort);
}
}
}
return err;
}
I actually save IP (address.sin_addr.s_addr) in a long data type that is a private variable, m_nIP. problem was i didnt need the IPv6 as our entire product groups use IPv4. Solved this using the code is below.
const uint8_t *bytes = ((const struct sockaddr_in6 *)addrPtr)->sin6_addr.s6_addr;
bytes += 12;
struct in_addr addr = { *(const in_addr_t *)bytes };
m_nIP = ntohl(addr.s_addr);
RELEVANT GUIDES Beej's Guide to Network Programming, UserLevel IPv6 Intro, Porting Applications to IPv6

Linux sockets: Segmentation error

Linux n00b here. So about a month ago I installed emacs and the gcc/g++ compiler and have gotten started with programming. I found some code online for an echo server program, copied it and compiled it to test the networking functions. It compiled but then when I tried to run it I got the error message: Segmentation fault(core dumped). When I looked carefully at the debugger details it was an error in the "fwrite()" function. I linked the code to the library libstdc++.a upon compiling and creating the output file so it does make me wonder if there is some critical error in the actual library functions and I need to go back, find the function .c sourcecode, and then add them to the headers to make it work. The code is posted below. Anybody else had this problem?
#include <sys-socket.h> /* socket definitions */
#include <sys-types.h> /* socket types */
#include <netinet-in.h> /* inet (3) functions */
#include <unistd.h> /* misc. UNIX functions */
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ether.h>
#include <string.h>
/* Global constants */
#define ECHO_PORT 2002
#define MAX_LINE 1000
#define LISTENQ 5
ssize_t Readline(int sockd, char *vptr,size_t maxlen) {
ssize_t n, rc;
char* c;
msghdr* buffer;
buffer->msg_iov->iov_base = vptr;
buffer->msg_iov->iov_len = maxlen;
for ( n = 1; n < maxlen; n++ ) {
if ( (rc = recvmsg(sockd,buffer, 1)) == 1 ) {
c = buffer->msg_iov->iov_base++;
if (*c == '\n' )
break;
}
else if ( rc == 0 ) {
if ( n == 1 )
return 0;
else
break;
}
else {
if (rc < 0 )
continue;
return -1;
}
}
buffer->msg_iov->iov_base = 0;
return n;
}
/* Write a line to a socket */
ssize_t Writeline(int sockd, char *vptr) {
msghdr *buffer;
buffer->msg_iov->iov_base = vptr;
size_t nleft = buffer->msg_iov->iov_len;
ssize_t nwritten;
while ( nleft > 0 ) {
if ( (nwritten = sendmsg(sockd, buffer, nleft)) < 0 ) {
return -1;
}
nleft -= nwritten;
buffer += nwritten;
}
return nwritten;
}
int main(int argc, char *argv[]) {
int list_s; /* listening socket */
int conn_s; /* connection socket */
short int port; /* port number */
struct sockaddr_in servaddr; /* socket address structure */
char *endptr; /* for strtol() */
char buffer[MAX_LINE];
port = 5000;
/* Create the listening socket */
if ( (list_s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
fprintf(stderr, "ECHOSERV: Error creating listening socket.\n");
exit(EXIT_FAILURE);
}
/* Set all bytes in socket address structure to
zero, and fill in the relevant data members */
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
/* Bind our socket addresss to the
listening socket, and call listen() */
if ( bind(list_s, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling bind()\n");
exit(EXIT_FAILURE);
}
if ( listen(list_s, LISTENQ) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling listen()\n");
exit(EXIT_FAILURE);
}
/* Enter an infinite loop to respond
to client requests and echo input */
while ( 1 ) {
/* Wait for a connection, then accept() it */
if ( (conn_s = accept(list_s, NULL, NULL) ) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling accept()\n");
exit(EXIT_FAILURE);
}
/* Retrieve an input line from the connected socket
then simply write it back to the same socket. */
Readline(conn_s, buffer, MAX_LINE-1);
Writeline(conn_s, buffer);
/* Close the connected socket */
if ( shutdown(conn_s,0) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling close()\n");
exit(EXIT_FAILURE);
}
}
}
ssize_t Writeline(int sockd, char *vptr) {
msghdr *buffer;
buffer->msg_iov->iov_base = vptr;
your pointer buffer is not initialized. You might look at this code snippet to do it correctly:
/* This structure contains parameter information for sendmsg. */
struct msghdr mh;
/* The message header contains parameters for sendmsg. */
mh.msg_name = (caddr_t) &dest;
mh.msg_namelen = sizeof(dest);
mh.msg_iov = iov;
mh.msg_iovlen = 3;
mh.msg_accrights = NULL; /* irrelevant to AF_INET */
mh.msg_accrightslen = 0; /* irrelevant to AF_INET */
rc = sendmsg(s, &mh, 0); /* no flags used */
if (rc == -1) {
perror("sendmsg failed");
return -1;
}
return 0;
}
sendmsg
You are not setting the buffer pointer variables in your Writeline() and ReadLine() functions.
ssize_t Writeline(int sockd, char *vptr) {
msghdr *buffer;
//this is not appropriate as buffer does not point to appropriate memory.
buffer->msg_iov->iov_base = vptr;
size_t nleft = buffer->msg_iov->iov_len;
ssize_t nwritten;
...
return nwritten;
}
Accessing buffer->msg_iov->iov_base or buffer->msg_iov->iov_len or even buffer is not appropriate without allocating it or setting to appropriate memory is not valid.
Did you actually COPY this, or did you copy bits and then paste it together yourself?
It's not very hard to fix at least to the point where it doesn't crash by itself - I didn't get further because my firewall settings are too strict to just fire up a program and use a random port, and I don't feel like messing up my firewall setting just to test your code.
So, as pointed out msghdr *buffer; means that the pointer for buffer is uninitialized. The easy fix is to NOT use a pointer, and instead use the address of buffer when you need it. You then need to have an iov data structure.
So, in receive, you end up with something like this:
msghdr buffer;
iovec iov;
buffer.msg_iov = &iov;
...
if ( (rc = recvmsg(sockd, &buffer, 1)) == 1 ) {
c = vptr++;
buffer.msg_iov->iov_base = vptr;
Note the & in front of buffer. I also changed the next line, as it was doing ++ on a void pointer, which is not clearly defined in C++, so the compiler gave a warning. (There's also a warning for buffer not initialized).
A similar treatment is needed in the `WriteLine function.
iovec iov;
buffer.msg_iov = &iov;
buffer.msg_iov->iov_base = vptr;
size_t nleft = MAX_LINE;
...
if ( (nwritten = sendmsg(sockd, &buffer, nleft)) < 0 ) {
....
nleft -= nwritten;
vptr += nwritten;
buffer.msg_iov->iov_base = vptr;
Again, the increment of iov_base is incrementing a void *, which hasn't been defined since I wrote it above, so need to make sure that the pointer has a different type - reusing vptr is decent here.
As an aside, I changed the nleft to set to MAX_LINE, as you don't pass in the size of the line. I would suggest that you change it so that it does take the size as an argument, similar to the ReadLine function.
Finally, please do yourself a favour and use -Wall -Werror when compiling the code - that means that you will get warnings when you do "silly" things - it may work, but it may also NOT work. Nearly all warnings from the compiler are USEFUL.
Remember when using a pointer in C or C++, you should make sure it points at something. Just writing T* ptr; only gives you a pointer, there is no memory attached to the pointer, so before you USE that pointer, you should assign it in some way.
I'm far from convinced this covers everything - but it should get you somewhat on the way to getting something working.

simple program using libnetfilter_queue isn't working

I'm trying to learn how to use libnetfilter_queue. I've compiled the example provided with the library.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/types.h>
#include <linux/netfilter.h> /* for NF_ACCEPT */
#include <libnetfilter_queue/libnetfilter_queue.h>
/* returns packet id */
static u_int32_t print_pkt (struct nfq_data *tb)
{
int id = 0;
struct nfqnl_msg_packet_hdr *ph;
struct nfqnl_msg_packet_hw *hwph;
u_int32_t mark,ifi;
int ret;
char *data;
ph = nfq_get_msg_packet_hdr(tb);
if (ph) {
id = ntohl(ph->packet_id);
printf("hw_protocol=0x%04x hook=%u id=%u ",
ntohs(ph->hw_protocol), ph->hook, id);
}
hwph = nfq_get_packet_hw(tb);
if (hwph) {
int i, hlen = ntohs(hwph->hw_addrlen);
printf("hw_src_addr=");
for (i = 0; i < hlen-1; i++)
printf("%02x:", hwph->hw_addr[i]);
printf("%02x ", hwph->hw_addr[hlen-1]);
}
mark = nfq_get_nfmark(tb);
if (mark)
printf("mark=%u ", mark);
ifi = nfq_get_indev(tb);
if (ifi)
printf("indev=%u ", ifi);
ifi = nfq_get_outdev(tb);
if (ifi)
printf("outdev=%u ", ifi);
ifi = nfq_get_physindev(tb);
if (ifi)
printf("physindev=%u ", ifi);
ifi = nfq_get_physoutdev(tb);
if (ifi)
printf("physoutdev=%u ", ifi);
ret = nfq_get_payload(tb, &data);
if (ret >= 0)
printf("payload_len=%d ", ret);
fputc('\n', stdout);
return id;
}
static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
struct nfq_data *nfa, void *data)
{
u_int32_t id = print_pkt(nfa);
printf("entering callback\n");
return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
}
int main(int argc, char **argv)
{
struct nfq_handle *h;
struct nfq_q_handle *qh;
int fd;
int rv;
char buf[4096] __attribute__ ((aligned));
printf("opening library handle\n");
h = nfq_open();
if (!h) {
fprintf(stderr, "error during nfq_open()\n");
exit(1);
}
printf("unbinding existing nf_queue handler for AF_INET (if any)\n");
if (nfq_unbind_pf(h, AF_INET) < 0) {
fprintf(stderr, "error during nfq_unbind_pf()\n");
exit(1);
}
printf("binding nfnetlink_queue as nf_queue handler for AF_INET\n");
if (nfq_bind_pf(h, AF_INET) < 0) {
fprintf(stderr, "error during nfq_bind_pf()\n");
exit(1);
}
printf("binding this socket to queue '0'\n");
qh = nfq_create_queue(h, 1, &cb, NULL);
if (!qh) {
fprintf(stderr, "error during nfq_create_queue()\n");
exit(1);
}
printf("setting copy_packet mode\n");
if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
fprintf(stderr, "can't set packet_copy mode\n");
exit(1);
}
fd = nfq_fd(h);
while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
printf("pkt received\n");
nfq_handle_packet(h, buf, rv);
}
printf("unbinding from queue 0\n");
nfq_destroy_queue(qh);
#ifdef INSANE
/* normally, applications SHOULD NOT issue this command, since
* it detaches other programs/sockets from AF_INET, too ! */
printf("unbinding from AF_INET\n");
nfq_unbind_pf(h, AF_INET);
#endif
printf("closing library handle\n");
nfq_close(h);
exit(0);
}
Launching the executable it doesn't receive anything.
I also followed this thread and tried to use netcat to generate traffic but again i didn't see any packet
Maybe I'm missing something to really interact with filter queues?
Yes, you have. If your firewall lets the packets trough the rules nothing will happen and the packets wont left the kernel space. But if you setting up some rules that send the packets to the user space, your programm will work.
iptables -A INPUT -i eth0 -j NFQUEUE --queue-num 0
This line will send all packets from the INPUT with the --in-interface eth0 into the userspace.
libnetfilter_queue is as it's named, a filter. That means, it filters out traffic based on the settings you give it when you create and open the queue.
If you do not send any traffic through this filter, the filter has nothing to work on. How much (what kind of) traffic that goes through this filter, you must set in iptable.
Iptable works as a firewall into, through or out of your system. Libnetfilter_queue lets you control what to do with the filtered traffic.
Hope this helps you somewhat.
Reading that thread I found I haven't added iptables rules... Adding those rules make the trick.
So I always need to set iptables rules to get packets using netfilter library?

How do you make a HTTP request with C++?

Is there any way to easily make a HTTP request with C++? Specifically, I want to download the contents of a page (an API) and check the contents to see if it contains a 1 or a 0. Is it also possible to download the contents into a string?
I had the same problem. libcurl is really complete. There is a C++ wrapper curlpp that might interest you as you ask for a C++ library. neon is another interesting C library that also support WebDAV.
curlpp seems natural if you use C++. There are many examples provided in the source distribution.
To get the content of an URL you do something like that (extracted from examples) :
// Edit : rewritten for cURLpp 0.7.3
// Note : namespace changed, was cURLpp in 0.7.2 ...
#include <curlpp/cURLpp.hpp>
#include <curlpp/Options.hpp>
// RAII cleanup
curlpp::Cleanup myCleanup;
// Send request and get a result.
// Here I use a shortcut to get it in a string stream ...
std::ostringstream os;
os << curlpp::options::Url(std::string("http://example.com"));
string asAskedInQuestion = os.str();
See the examples directory in curlpp source distribution, there is a lot of more complex cases, as well as a simple complete minimal one using curlpp.
my 2 cents ...
Windows code:
#include <string.h>
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#include <vector>
#include <locale>
#include <sstream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
int main( void ){
WSADATA wsaData;
SOCKET Socket;
SOCKADDR_IN SockAddr;
int lineCount=0;
int rowCount=0;
struct hostent *host;
locale local;
char buffer[10000];
int i = 0 ;
int nDataLength;
string website_HTML;
// website url
string url = "www.google.com";
//HTTP GET
string get_http = "GET / HTTP/1.1\r\nHost: " + url + "\r\nConnection: close\r\n\r\n";
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0){
cout << "WSAStartup failed.\n";
system("pause");
//return 1;
}
Socket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
host = gethostbyname(url.c_str());
SockAddr.sin_port=htons(80);
SockAddr.sin_family=AF_INET;
SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr)) != 0){
cout << "Could not connect";
system("pause");
//return 1;
}
// send GET / HTTP
send(Socket,get_http.c_str(), strlen(get_http.c_str()),0 );
// recieve html
while ((nDataLength = recv(Socket,buffer,10000,0)) > 0){
int i = 0;
while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r'){
website_HTML+=buffer[i];
i += 1;
}
}
closesocket(Socket);
WSACleanup();
// Display HTML source
cout<<website_HTML;
// pause
cout<<"\n\nPress ANY key to close.\n\n";
cin.ignore(); cin.get();
return 0;
}
Here is a much better implementation:
#include <windows.h>
#include <string>
#include <stdio.h>
using std::string;
#pragma comment(lib,"ws2_32.lib")
HINSTANCE hInst;
WSADATA wsaData;
void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename);
SOCKET connectToServer(char *szServerName, WORD portNum);
int getHeaderLength(char *content);
char *readUrl2(char *szUrl, long &bytesReturnedOut, char **headerOut);
int main()
{
const int bufLen = 1024;
char *szUrl = "http://stackoverflow.com";
long fileSize;
char *memBuffer, *headerBuffer;
FILE *fp;
memBuffer = headerBuffer = NULL;
if ( WSAStartup(0x101, &wsaData) != 0)
return -1;
memBuffer = readUrl2(szUrl, fileSize, &headerBuffer);
printf("returned from readUrl\n");
printf("data returned:\n%s", memBuffer);
if (fileSize != 0)
{
printf("Got some data\n");
fp = fopen("downloaded.file", "wb");
fwrite(memBuffer, 1, fileSize, fp);
fclose(fp);
delete(memBuffer);
delete(headerBuffer);
}
WSACleanup();
return 0;
}
void mParseUrl(char *mUrl, string &serverName, string &filepath, string &filename)
{
string::size_type n;
string url = mUrl;
if (url.substr(0,7) == "http://")
url.erase(0,7);
if (url.substr(0,8) == "https://")
url.erase(0,8);
n = url.find('/');
if (n != string::npos)
{
serverName = url.substr(0,n);
filepath = url.substr(n);
n = filepath.rfind('/');
filename = filepath.substr(n+1);
}
else
{
serverName = url;
filepath = "/";
filename = "";
}
}
SOCKET connectToServer(char *szServerName, WORD portNum)
{
struct hostent *hp;
unsigned int addr;
struct sockaddr_in server;
SOCKET conn;
conn = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (conn == INVALID_SOCKET)
return NULL;
if(inet_addr(szServerName)==INADDR_NONE)
{
hp=gethostbyname(szServerName);
}
else
{
addr=inet_addr(szServerName);
hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
}
if(hp==NULL)
{
closesocket(conn);
return NULL;
}
server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
server.sin_family=AF_INET;
server.sin_port=htons(portNum);
if(connect(conn,(struct sockaddr*)&server,sizeof(server)))
{
closesocket(conn);
return NULL;
}
return conn;
}
int getHeaderLength(char *content)
{
const char *srchStr1 = "\r\n\r\n", *srchStr2 = "\n\r\n\r";
char *findPos;
int ofset = -1;
findPos = strstr(content, srchStr1);
if (findPos != NULL)
{
ofset = findPos - content;
ofset += strlen(srchStr1);
}
else
{
findPos = strstr(content, srchStr2);
if (findPos != NULL)
{
ofset = findPos - content;
ofset += strlen(srchStr2);
}
}
return ofset;
}
char *readUrl2(char *szUrl, long &bytesReturnedOut, char **headerOut)
{
const int bufSize = 512;
char readBuffer[bufSize], sendBuffer[bufSize], tmpBuffer[bufSize];
char *tmpResult=NULL, *result;
SOCKET conn;
string server, filepath, filename;
long totalBytesRead, thisReadSize, headerLen;
mParseUrl(szUrl, server, filepath, filename);
///////////// step 1, connect //////////////////////
conn = connectToServer((char*)server.c_str(), 80);
///////////// step 2, send GET request /////////////
sprintf(tmpBuffer, "GET %s HTTP/1.0", filepath.c_str());
strcpy(sendBuffer, tmpBuffer);
strcat(sendBuffer, "\r\n");
sprintf(tmpBuffer, "Host: %s", server.c_str());
strcat(sendBuffer, tmpBuffer);
strcat(sendBuffer, "\r\n");
strcat(sendBuffer, "\r\n");
send(conn, sendBuffer, strlen(sendBuffer), 0);
// SetWindowText(edit3Hwnd, sendBuffer);
printf("Buffer being sent:\n%s", sendBuffer);
///////////// step 3 - get received bytes ////////////////
// Receive until the peer closes the connection
totalBytesRead = 0;
while(1)
{
memset(readBuffer, 0, bufSize);
thisReadSize = recv (conn, readBuffer, bufSize, 0);
if ( thisReadSize <= 0 )
break;
tmpResult = (char*)realloc(tmpResult, thisReadSize+totalBytesRead);
memcpy(tmpResult+totalBytesRead, readBuffer, thisReadSize);
totalBytesRead += thisReadSize;
}
headerLen = getHeaderLength(tmpResult);
long contenLen = totalBytesRead-headerLen;
result = new char[contenLen+1];
memcpy(result, tmpResult+headerLen, contenLen);
result[contenLen] = 0x0;
char *myTmp;
myTmp = new char[headerLen+1];
strncpy(myTmp, tmpResult, headerLen);
myTmp[headerLen] = NULL;
delete(tmpResult);
*headerOut = myTmp;
bytesReturnedOut = contenLen;
closesocket(conn);
return(result);
}
Update 2020: I have a new answer that replaces this, now 8-years-old, one: https://stackoverflow.com/a/61177330/278976
On Linux, I tried cpp-netlib, libcurl, curlpp, urdl, boost::asio and considered Qt (but turned it down based on the license). All of these were either incomplete for this use, had sloppy interfaces, had poor documentation, were unmaintained or didn't support https.
Then, at the suggestion of https://stackoverflow.com/a/1012577/278976, I tried POCO. Wow, I wish I had seen this years ago. Here's an example of making an HTTP GET request with POCO:
https://stackoverflow.com/a/26026828/2817595
POCO is free, open source (boost license). And no, I don't have any affiliation with the company; I just really like their interfaces. Great job guys (and gals).
https://pocoproject.org/download.html
Hope this helps someone... it took me three days to try all of these libraries out.
There is a newer, less mature curl wrapper being developed called C++ Requests. Here's a simple GET request:
#include <iostream>
#include <cpr.h>
int main(int argc, char** argv) {
auto response = cpr::Get(cpr::Url{"http://httpbin.org/get"});
std::cout << response.text << std::endl;
}
It supports a wide variety of HTTP verbs and curl options. There's more usage documentation here.
Disclaimer: I'm the maintainer of this library.
Updated answer for April, 2020:
I've had a lot of success, recently, with cpp-httplib (both as a client and a server). It's mature and its approximate, single-threaded RPS is around 6k.
On more of the bleeding edge, there's a really promising framework, cpv-framework, that can get around 180k RPS on two cores (and will scale well with the number of cores because it's based on the seastar framework, which powers the fastest DBs on the planet, scylladb).
However, cpv-framework is still relatively immature; so, for most uses, I highly recommend cpp-httplib.
This recommendation replaces my previous answer (8 years ago).
Here is my minimal wrapper around cURL to be able just to fetch a webpage as a string. This is useful, for example, for unit testing. It is basically a RAII wrapper around the C code.
Install "libcurl" on your machine yum install libcurl libcurl-devel or equivalent.
Usage example:
CURLplusplus client;
string x = client.Get("http://google.com");
string y = client.Get("http://yahoo.com");
Class implementation:
#include <curl/curl.h>
class CURLplusplus
{
private:
CURL* curl;
stringstream ss;
long http_code;
public:
CURLplusplus()
: curl(curl_easy_init())
, http_code(0)
{
}
~CURLplusplus()
{
if (curl) curl_easy_cleanup(curl);
}
std::string Get(const std::string& url)
{
CURLcode res;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
ss.str("");
http_code = 0;
res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
throw std::runtime_error(curl_easy_strerror(res));
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
return ss.str();
}
long GetHttpCode()
{
return http_code;
}
private:
static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
return static_cast<CURLplusplus*>(userp)->Write(buffer,size,nmemb);
}
size_t Write(void *buffer, size_t size, size_t nmemb)
{
ss.write((const char*)buffer,size*nmemb);
return size*nmemb;
}
};
As you want a C++ solution, you could use Qt. It has a QHttp class you can use.
You can check the docs:
http->setHost("qt.nokia.com");
http->get(QUrl::toPercentEncoding("/index.html"));
Qt also has a lot more to it that you could use in a common C++ app.
You may want to check C++ REST SDK (codename "Casablanca"). http://msdn.microsoft.com/en-us/library/jj950081.aspx
With the C++ REST SDK, you can more easily connect to HTTP servers from your C++ app.
Usage example:
#include <iostream>
#include <cpprest/http_client.h>
using namespace web::http; // Common HTTP functionality
using namespace web::http::client; // HTTP client features
int main(int argc, char** argv) {
http_client client("http://httpbin.org/");
http_response response;
// ordinary `get` request
response = client.request(methods::GET, "/get").get();
std::cout << response.extract_string().get() << "\n";
// working with json
response = client.request(methods::GET, "/get").get();
std::cout << "url: " << response.extract_json().get()[U("url")] << "\n";
}
The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design.
libCURL is a pretty good option for you. Depending on what you need to do, the tutorial should tell you what you want, specifically for the easy handle. But, basically, you could do this just to see the source of a page:
CURL* c;
c = curl_easy_init();
curl_easy_setopt( c, CURL_URL, "www.google.com" );
curl_easy_perform( c );
curl_easy_cleanup( c );
I believe this will cause the result to be printed to stdout. If you want to handle it instead -- which, I assume, you do -- you need to set the CURL_WRITEFUNCTION. All of that is covered in the curl tutorial linked above.
With this answer I refer to the answer from Software_Developer. By rebuilding the code I found that some parts are deprecated (gethostbyname()) or do not provide error handling (creation of sockets, sending something) for an operation.
The following windows code is tested with Visual Studio 2013 and Windows 8.1 64-bit as well as Windows 7 64-bit. It will target an IPv4 TCP Connection with the Web Server of www.google.com.
#include <winsock2.h>
#include <WS2tcpip.h>
#include <windows.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main (){
// Initialize Dependencies to the Windows Socket.
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
cout << "WSAStartup failed.\n";
system("pause");
return -1;
}
// We first prepare some "hints" for the "getaddrinfo" function
// to tell it, that we are looking for a IPv4 TCP Connection.
struct addrinfo hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; // We are targeting IPv4
hints.ai_protocol = IPPROTO_TCP; // We are targeting TCP
hints.ai_socktype = SOCK_STREAM; // We are targeting TCP so its SOCK_STREAM
// Aquiring of the IPv4 address of a host using the newer
// "getaddrinfo" function which outdated "gethostbyname".
// It will search for IPv4 addresses using the TCP-Protocol.
struct addrinfo* targetAdressInfo = NULL;
DWORD getAddrRes = getaddrinfo("www.google.com", NULL, &hints, &targetAdressInfo);
if (getAddrRes != 0 || targetAdressInfo == NULL)
{
cout << "Could not resolve the Host Name" << endl;
system("pause");
WSACleanup();
return -1;
}
// Create the Socket Address Informations, using IPv4
// We dont have to take care of sin_zero, it is only used to extend the length of SOCKADDR_IN to the size of SOCKADDR
SOCKADDR_IN sockAddr;
sockAddr.sin_addr = ((struct sockaddr_in*) targetAdressInfo->ai_addr)->sin_addr; // The IPv4 Address from the Address Resolution Result
sockAddr.sin_family = AF_INET; // IPv4
sockAddr.sin_port = htons(80); // HTTP Port: 80
// We have to free the Address-Information from getaddrinfo again
freeaddrinfo(targetAdressInfo);
// Creation of a socket for the communication with the Web Server,
// using IPv4 and the TCP-Protocol
SOCKET webSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (webSocket == INVALID_SOCKET)
{
cout << "Creation of the Socket Failed" << endl;
system("pause");
WSACleanup();
return -1;
}
// Establishing a connection to the web Socket
cout << "Connecting...\n";
if(connect(webSocket, (SOCKADDR*)&sockAddr, sizeof(sockAddr)) != 0)
{
cout << "Could not connect";
system("pause");
closesocket(webSocket);
WSACleanup();
return -1;
}
cout << "Connected.\n";
// Sending a HTTP-GET-Request to the Web Server
const char* httpRequest = "GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n";
int sentBytes = send(webSocket, httpRequest, strlen(httpRequest),0);
if (sentBytes < strlen(httpRequest) || sentBytes == SOCKET_ERROR)
{
cout << "Could not send the request to the Server" << endl;
system("pause");
closesocket(webSocket);
WSACleanup();
return -1;
}
// Receiving and Displaying an answer from the Web Server
char buffer[10000];
ZeroMemory(buffer, sizeof(buffer));
int dataLen;
while ((dataLen = recv(webSocket, buffer, sizeof(buffer), 0) > 0))
{
int i = 0;
while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r') {
cout << buffer[i];
i += 1;
}
}
// Cleaning up Windows Socket Dependencies
closesocket(webSocket);
WSACleanup();
system("pause");
return 0;
}
References:
Deprecation of gethostbyname
Return Value of socket()
Return Value of send()
C++ does not provide any way to do it directly. It would entirely depend on what platforms and libraries that you have.
At worst case, you can use the boost::asio library to establish a TCP connection, send the HTTP headers (RFC 2616), and parse the responses directly. Looking at your application needs, this is simple enough to do.
Note that this does not require libcurl, Windows.h, or WinSock! No compilation of libraries, no project configuration, etc. I have this code working in Visual Studio 2017 c++ on Windows 10:
#pragma comment(lib, "urlmon.lib")
#include <urlmon.h>
#include <sstream>
using namespace std;
...
IStream* stream;
//Also works with https URL's - unsure about the extent of SSL support though.
HRESULT result = URLOpenBlockingStream(0, "http://google.com", &stream, 0, 0);
if (result != 0)
{
return 1;
}
char buffer[100];
unsigned long bytesRead;
stringstream ss;
stream->Read(buffer, 100, &bytesRead);
while (bytesRead > 0U)
{
ss.write(buffer, (long long)bytesRead);
stream->Read(buffer, 100, &bytesRead);
}
stream->Release();
string resultString = ss.str();
I just figured out how to do this, as I wanted a simple API access script, libraries like libcurl were causing me all kinds of problems (even when I followed the directions...), and WinSock is just too low-level and complicated.
I'm not quite sure about all of the IStream reading code (particularly the while condition - feel free to correct/improve), but hey, it works, hassle free! (It makes sense to me that, since I used a blocking (synchronous) call, this is fine, that bytesRead would always be > 0U until the stream (ISequentialStream?) is finished being read, but who knows.)
See also: URL Monikers and Asynchronous Pluggable Protocol Reference
Here is some code that will work with no need to use any 3rd party library:
First define your gateway, user, password and any other parameters you need to send to this specific server.
#define USERNAME "user"
#define PASSWORD "your password"
#define GATEWAY "your gateway"
Here is the code itself:
HINTERNET hOpenHandle, hResourceHandle, hConnectHandle;
const TCHAR* szHeaders = _T("Content-Type:application/json; charset=utf-8\r\n");
hOpenHandle = InternetOpen(_T("HTTPS"), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
if (hOpenHandle == NULL)
{
return false;
}
hConnectHandle = InternetConnect(hOpenHandle,
GATEWAY,
INTERNET_DEFAULT_HTTPS_PORT,
NULL, NULL, INTERNET_SERVICE_HTTP,
0, 1);
if (hConnectHandle == NULL)
{
InternetCloseHandle(hOpenHandle);
return false;
}
hResourceHandle = HttpOpenRequest(hConnectHandle,
_T("POST"),
GATEWAY,
NULL, NULL, NULL, INTERNET_FLAG_SECURE | INTERNET_FLAG_KEEP_CONNECTION,
1);
if (hResourceHandle == NULL)
{
InternetCloseHandle(hOpenHandle);
InternetCloseHandle(hConnectHandle);
return false;
}
InternetSetOption(hResourceHandle, INTERNET_OPTION_USERNAME, (LPVOID)USERNAME, _tcslen(USERNAME));
InternetSetOption(hResourceHandle, INTERNET_OPTION_PASSWORD, (LPVOID)PASSWORD, _tcslen(PASSWORD));
std::string buf;
if (HttpSendRequest(hResourceHandle, szHeaders, 0, NULL, 0))
{
while (true)
{
std::string part;
DWORD size;
if (!InternetQueryDataAvailable(hResourceHandle, &size, 0, 0))break;
if (size == 0)break;
part.resize(size);
if (!InternetReadFile(hResourceHandle, &part[0], part.size(), &size))break;
if (size == 0)break;
part.resize(size);
buf.append(part);
}
}
if (!buf.empty())
{
// Get data back
}
InternetCloseHandle(hResourceHandle);
InternetCloseHandle(hConnectHandle);
InternetCloseHandle(hOpenHandle);
That should work on a Win32 API environment.
Here is an example.
C and C++ don't have a standard library for HTTP or even for socket connections. Over the years some portable libraries have been developed. The most widely used, as others have said, is libcurl.
Here is a list of alternatives to libcurl (coming from the libcurl's web site).
Also, for Linux, this is a simple HTTP client. You could implement your own simple HTTP GET client, but this won't work if there are authentication or redirects involved or if you need to work behind a proxy. For these cases you need a full-blown library like libcurl.
For source code with libcurl, this is the closest to what you want (Libcurl has many examples). Look at the main function. The html content will be copied to the buffer, after a successfully connection. Just replace parseHtml with your own function.
The HTTP protocol is very simple, so it is very simple to write a HTTP client.
Here is one
https://github.com/pedro-vicente/lib_netsockets
It uses HTTP GET to retrieve a file from a web server, both server and file are command line parameters. The remote file is saved to a local copy.
Disclaimer: I am the author
check http.cc
https://github.com/pedro-vicente/lib_netsockets/blob/master/src/http.cc
int http_client_t::get(const char *path_remote_file)
{
char buf_request[1024];
//construct request message using class input parameters
sprintf(buf_request, "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", path_remote_file, m_server_ip.c_str());
//send request, using built in tcp_client_t socket
if (this->write_all(buf_request, (int)strlen(buf_request)) < 0)
{
return -1;
}
EDIT: edited URL
You can use embeddedRest library. It is lightweight header-only library. So it is easy to include it to your project and it does not require compilation cause there no .cpp files in it.
Request example from readme.md from repo:
#include "UrlRequest.hpp"
//...
UrlRequest request;
request.host("api.vk.com");
const auto countryId = 1;
const auto count = 1000;
request.uri("/method/database.getCities",{
{ "lang", "ru" },
{ "country_id", countryId },
{ "count", count },
{ "need_all", "1" },
});
request.addHeader("Content-Type: application/json");
auto response = std::move(request.perform());
if (response.statusCode() == 200) {
cout << "status code = " << response.statusCode() << ", body = *" << response.body() << "*" << endl;
}else{
cout << "status code = " << response.statusCode() << ", description = " << response.statusDescription() << endl;
}
Here is some (relatively) simple C++11 code that uses libCURL to download a URL's content into a std::vector<char>:
http_download.hh
# pragma once
#include <string>
#include <vector>
std::vector<char> download(std::string url, long* responseCode = nullptr);
http_download.cc
#include "http_download.hh"
#include <curl/curl.h>
#include <sstream>
#include <stdexcept>
using namespace std;
size_t callback(void* contents, size_t size, size_t nmemb, void* user)
{
auto chunk = reinterpret_cast<char*>(contents);
auto buffer = reinterpret_cast<vector<char>*>(user);
size_t priorSize = buffer->size();
size_t sizeIncrease = size * nmemb;
buffer->resize(priorSize + sizeIncrease);
std::copy(chunk, chunk + sizeIncrease, buffer->data() + priorSize);
return sizeIncrease;
}
vector<char> download(string url, long* responseCode)
{
vector<char> data;
curl_global_init(CURL_GLOBAL_ALL);
CURL* handle = curl_easy_init();
curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, callback);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &data);
curl_easy_setopt(handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
CURLcode result = curl_easy_perform(handle);
if (responseCode != nullptr)
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, responseCode);
curl_easy_cleanup(handle);
curl_global_cleanup();
if (result != CURLE_OK)
{
stringstream err;
err << "Error downloading from URL \"" << url << "\": " << curl_easy_strerror(result);
throw runtime_error(err.str());
}
return data;
}
If you are looking for a HTTP client library in C++ that is supported in multiple platforms (Linux, Windows and Mac) for consuming Restful web services. You can have below options.
QT Network Library - Allows the application to send network requests and receive replies
C++ REST SDK - An emerging third-party HTTP library with PPL support
Libcurl - It is probably one of the most used http lib in the native world.
Is there any way to easily make a HTTP request with C++? Specifically, I want to download the contents of a page (an API) and check the contents to see if it contains a 1 or a 0. Is it also possible to download the contents into a string?
First off ... I know this question is 12 years old. However . None of the answers provided gave an example that was "simple" without the need to build some external library
Below is the most simple solution I could come up with to retrieve and print the contents of a webpage.
Some Documentation on the functions utilized in the example below
// wininet lib :
https://learn.microsoft.com/en-us/windows/win32/api/wininet/
// wininet->internetopena();
https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetopena
// wininet->intenetopenurla();
https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetopenurla
// wininet->internetreadfile();
https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetreadfile
// wininet->internetclosehandle();
https://learn.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-internetclosehandle
#include <iostream>
#include <WinSock2.h>
#include <wininet.h>
#pragma comment(lib, "wininet.lib")
int main()
{
// ESTABLISH SOME LOOSE VARIABLES
const int size = 4096;
char buf[size];
DWORD length;
// ESTABLISH CONNECTION TO THE INTERNET
HINTERNET internet = InternetOpenA("Mozilla/5.0", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, NULL);
if (!internet)
ExitProcess(EXIT_FAILURE); // Failed to establish connection to internet, Exit
// ATTEMPT TO CONNECT TO WEBSITE "google.com"
HINTERNET response = InternetOpenUrlA(internet, "http://www.google.com", NULL, NULL, NULL, NULL);
if (!response) {
// CONNECTION TO "google.com" FAILED
InternetCloseHandle(internet); // Close handle to internet
ExitProcess(EXIT_FAILURE);
}
// READ CONTENTS OF WEBPAGE IN HTML FORMAT
if (!InternetReadFile(response, buf, size, &length)) {
// FAILED TO READ CONTENTS OF WEBPAGE
// Close handles and Exit
InternetCloseHandle(response); // Close handle to response
InternetCloseHandle(internet); // Close handle to internet
ExitProcess(EXIT_FAILURE);
}
// CLOSE HANDLES AND OUTPUT CONTENTS OF WEBPAGE
InternetCloseHandle(response); // Close handle to response
InternetCloseHandle(internet); // Close handle to internet
std::cout << buf << std::endl;
return 0;
}
Generally I'd recommend something cross-platform like cURL, POCO, or Qt. However, here is a Windows example!:
#include <atlbase.h>
#include <msxml6.h>
#include <comutil.h> // _bstr_t
HRESULT hr;
CComPtr<IXMLHTTPRequest> request;
hr = request.CoCreateInstance(CLSID_XMLHTTP60);
hr = request->open(
_bstr_t("GET"),
_bstr_t("https://www.google.com/images/srpr/logo11w.png"),
_variant_t(VARIANT_FALSE),
_variant_t(),
_variant_t());
hr = request->send(_variant_t());
// get status - 200 if succuss
long status;
hr = request->get_status(&status);
// load image data (if url points to an image)
VARIANT responseVariant;
hr = request->get_responseStream(&responseVariant);
IStream* stream = (IStream*)responseVariant.punkVal;
CImage *image = new CImage();
image->Load(stream);
stream->Release();
Although a little bit late. You may prefer https://github.com/Taymindis/backcurl .
It allows you to do http call on mobile c++ development. Suitable for Mobile game developement
bcl::init(); // init when using
bcl::execute<std::string>([&](bcl::Request *req) {
bcl::setOpts(req, CURLOPT_URL , "http://www.google.com",
CURLOPT_FOLLOWLOCATION, 1L,
CURLOPT_WRITEFUNCTION, &bcl::writeContentCallback,
CURLOPT_WRITEDATA, req->dataPtr,
CURLOPT_USERAGENT, "libcurl-agent/1.0",
CURLOPT_RANGE, "0-200000"
);
}, [&](bcl::Response * resp) {
std::string ret = std::string(resp->getBody<std::string>()->c_str());
printf("Sync === %s\n", ret.c_str());
});
bcl::cleanUp(); // clean up when no more using
All the answers above are helpful. My answer just adds some additions:
Use boost beast, sync example, async example, ssl example
Use nghttp2, example, It supports SSL, HTTP/2
Use Facebook proxygen, this project comprises the core C++ HTTP abstractions used at Facebook. It's aimed at high performance and concurrency. I recommend installing it with vcpkg or you will struggle with the dependencies management. It supports SSL. It also support some advanced protocols:HTTP/1.1, SPDY/3, SPDY/3.1, HTTP/2, and HTTP/3
Both nghttp2 and proxygen are stable, can be considered to use in production.
You can use ACE in order to do so:
#include "ace/SOCK_Connector.h"
int main(int argc, ACE_TCHAR* argv[])
{
//HTTP Request Header
char* szRequest = "GET /video/nice.mp4 HTTP/1.1\r\nHost: example.com\r\n\r\n";
int ilen = strlen(szRequest);
//our buffer
char output[16*1024];
ACE_INET_Addr server (80, "example.com");
ACE_SOCK_Stream peer;
ACE_SOCK_Connector connector;
int ires = connector.connect(peer, server);
int sum = 0;
peer.send(szRequest, ilen);
while (true)
{
ACE_Time_Value timeout = ACE_Time_Value(15);
int rc = peer.recv_n(output, 16*1024, &timeout);
if (rc == -1)
{
break;
}
sum += rc;
}
peer.close();
printf("Bytes transffered: %d",sum);
return 0;
}
CppRest SDK by MS is what I just found and after about 1/2 hour had my first simple web service call working. Compared that to others mentioned here where I was not able to get anything even installed after hours of looking, I'd say it is pretty impressive
https://github.com/microsoft/cpprestsdk
Scroll down and click on Documentation, then click on Getting Started Tutorial and you will have a simple app running in no time.
For the record cesanta's mongoose library seems to also support this: https://github.com/cesanta/mongoose/blob/6.17/examples/http_client/http_client.c