Been working on creating an IRC bot recently, and while it seems to connect & work with most IRC servers, it seems to have issues with the Twitch IRC server (irc.twitch.tv).
When connecting to another server, data is received & sent without any issues, however with the twitch connection, I can't send or receive data.
Initialize:
Connection::Connection(char* server, int port, std::string nick, std::string pass)
{
fServer = server;
fPort = port;
fNick = nick;
fPass = pass;
fChanCount = 0;
clear_buffer();
fSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
cout << "Attempting to resolve host: " << fServer << endl;
fHost = resolve_host(fServer);
fSaddr.sin_addr.S_un.S_addr = fHost;
fSaddr.sin_port = htons(fPort);
fSaddr.sin_family = AF_INET;
cout << "Resolved to: " << fHost << endl;
}
Open Connection:
int Connection::Connect()
{
if (connect(fSock, (sockaddr*)&fSaddr, sizeof(fSaddr)) == SOCKET_ERROR)
{
cout << "Can't connect to " << fServer << endl;
return 0;
}
recv(fSock, fBuffer, 1024 * 8, 0);
cout << fBuffer << endl << endl << flush;
send_message("PASS " + fPass);
send_message("NICK " + fNick);
send_message("USER " + fNick + " 0 * :" + fNick);
return 1;
}
Clear Buffer:
void Connection::clear_buffer()
{
memset(fBuffer, 0, sizeof(fBuffer));
}
Receive:
string Connection::receive_message()
{
clear_buffer();
recv(fSock, fBuffer, 1024 * 8, 0);
return fBuffer;
}
Completely stumped on what could be causing this, can provide more detail if needed.
Your USER message does not look like what the RFC2812 spec is asking for.
3.1.3 User message
Command: USER
Parameters: <user> <mode> <unused> <realname>
The USER command is used at the beginning of connection to specify
the username, hostname and realname of a new user.
The <mode> parameter should be a numeric, and can be used to
automatically set user modes when registering with the server. This
parameter is a bitmask, with only 2 bits having any signification: if
the bit 2 is set, the user mode 'w' will be set and if the bit 3 is
set, the user mode 'i' will be set. (See Section 3.1.5 "User
Modes").
The <realname> may contain space characters.
Numeric Replies:
ERR_NEEDMOREPARAMS ERR_ALREADYREGISTRED
Example:
USER guest 0 * :Ronnie Reagan ; User registering themselves with a
username of "guest" and real name
"Ronnie Reagan".
USER guest 8 * :Ronnie Reagan ; User registering themselves with a
username of "guest" and real name
"Ronnie Reagan", and asking to be set
invisible.
Perhaps that's what is causing the problem?
Otherwise I have found some servers require a password in the PASS message even if the server is not configured to require one. In those cases I send:
PASS none
Related
Edit: Upon Checking on IPv6 Test I Found out My ISP does not provide IPv6... otherwise this code is good
getaddrinfo() always fails for IPv6 with the error code 11268096, but it is successful for IPv4.
Setting Hint.ai_family = AF_INET6; is what triggers the error, but I do not know why.
Also, how do I get the sin_port/sin6_port in numbers? I am always getting port 0.(As #Remy Lebeau pointed out I am only asking for IP of the Domain so it won't output port...)
void GetAddrInfoFromHostNameIPV6(const char* DomainName, addrinfo* Result, bool& IsSuccessful)
{
IsSuccessful = false;
addrinfo Hint;
addrinfo* Return = nullptr;
int ErrorCode;
memset(&Hint, 0, sizeof(Hint));
Hint.ai_family = AF_INET6;
Hint.ai_socktype = SOCK_STREAM;
//Hint.ai_socktype = SOCK_DGRAM;
ErrorCode = getaddrinfo(DomainName, NULL, &Hint, &Return) << '\n';
if (ErrorCode != 0)
{
std::cout << "\n Error GetAddrInfoFromHostNameIPV6() Failed with Error Code: " << ErrorCode << " in GetAddrInfoFromHostName In: NW_P!";
}
else
{
*Result = *Return;
char IpAddress[INET6_ADDRSTRLEN];
uint16_t Port;
inet_ntop(AF_INET6, &((sockaddr_in6*)((Result)->ai_addr))->sin6_addr, IpAddress, INET6_ADDRSTRLEN);
Port = *(&((sockaddr_in6*)(Result->ai_addr))->sin6_port);
std::cout << "\n IPV6 Address of Domain '" << DomainName << "' Is " << IpAddress << " With Port: " << Port;
IsSuccessful = true;
}
if (!IsSuccessful)// For the safe of readability
{
std::cout << "\n Error GetAddrInfoFromHostName() Failed in NW_P!\n";
}
}
You are bit-shifting the error code 10 bits to the left before assigning it to ErrorCode.
Decimal 11268096 is binary 101010111111000000000000. Notice all those extra zeros on the right?
You need to get rid of << '\n' after getaddrinfo() returns, it doesn't belong there, as you are not outputting the error code to std::cout on that line of code.
Removing the bit shifting, the real error code is 11004 (binary 10101011111100) which is WSANO_DATA:
Valid name, no data record of requested type.The requested name is valid and was found in the database, but it does not have the correct associated data being resolved for. The usual example for this is a host name-to-address translation attempt (using gethostbyname or WSAAsyncGetHostByName) which uses the DNS (Domain Name Server). An MX record is returned but no A record—indicating the host itself exists, but is not directly reachable.
You can pass the error code to gai_strerror() to get a human-readable string for your error message output, eg:
std::cout << "\n Error GetAddrInfoFromHostNameIPV6() Failed with Error Code: " << ErrorCode << " (" << gai_strerror(ErrorCode) << ") in GetAddrInfoFromHostName In: NW_P!";
As for the port number being 0, you are not asking getaddrinfo() to parse any service name/port string as input (the pServiceName parameter is NULL), you are only asking for translating a domain name into an IP, so it is not going to output any port number. Port numbers are not used by Domains themselves. Port numbers are used by services (HTTP, etc) that are running on servers where domains/IPs point to.
On a side note, you are leaking the addrinfo list that getaddrinfo() outputs. You need to call freeaddrinfo() when you are done using the list.
I have a pretty basic live555 RTSP server and client to stream a h264 stream written in c++.
Here's the code I have for the client (adapted from testProgs/testRTSPClient.cpp, bundled with live555)
client->scheduler = BasicTaskScheduler::createNew();
client->env = BasicUsageEnvironment::createNew(*client->scheduler);
client->rtspClient = NULL;
RTSP_CLIENT::eventLoopWatchVariable = 0;
openURL(client, *client->env, string(string("rtsp://") + ip_address + ":" + to_string(BASE_RTSP_PORT + iris_id) + "/iris").c_str());
client->env->taskScheduler().doEventLoop(&RTSP_CLIENT::eventLoopWatchVariable);
void openURL(RTSP_CLIENT* client, UsageEnvironment& env, char const* rtspURL) {
// Begin by creating a "RTSPClient" object. Note that there is a separate "RTSPClient" object for each stream that we wish
// to receive (even if more than stream uses the same "rtsp://" URL).
while (!client->rtspClient) {
client->rtspClient = ourRTSPClient::createNew(env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, "main");
}
// Next, send a RTSP "DESCRIBE" command, to get a SDP description for the stream.
// Note that this command - like all RTSP commands - is sent asynchronously; we do not block, waiting for a response.
// Instead, the following function call returns immediately, and we handle the RTSP response later, from within the event loop:
client->rtspClient->sendDescribeCommand(continueAfterDESCRIBE);
}
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString) {
do {
UsageEnvironment& env = rtspClient->envir(); // alias
StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
if (resultCode != 0) {
env << *rtspClient << "Failed to get a SDP description: " << resultString << "\n";
delete[] resultString;
break;
}
char* const sdpDescription = resultString;
env << *rtspClient << "Got a SDP description:\n" << sdpDescription << "\n";
// Create a media session object from this SDP description:
scs.session = MediaSession::createNew(env, sdpDescription);
delete[] sdpDescription; // because we don't need it anymore
if (scs.session == NULL) {
env << *rtspClient << "Failed to create a MediaSession object from the SDP description: " << env.getResultMsg() << "\n";
break;
} else if (!scs.session->hasSubsessions()) {
env << *rtspClient << "This session has no media subsessions (i.e., no \"m=\" lines)\n";
break;
}
// Then, create and set up our data source objects for the session. We do this by iterating over the session's 'subsessions',
// calling "MediaSubsession::initiate()", and then sending a RTSP "SETUP" command, on each one.
// (Each 'subsession' will have its own data source.)
scs.iter = new MediaSubsessionIterator(*scs.session);
setupNextSubsession(rtspClient);
return;
} while (0);
// An unrecoverable error occurred with this stream.
shutdownStream(rtspClient);
}
Here's the code I have for the server (adapted from testProgs/testOnDemandRTSPServer.cpp, bundled with live555)
rtsp_server->taskSchedular = BasicTaskScheduler::createNew();
rtsp_server->usageEnvironment = BasicUsageEnvironment::createNew(*rtsp_server->taskSchedular);
rtsp_server->rtspServer = RTSPServer::createNew(*rtsp_server->usageEnvironment, BASE_RTSP_PORT + iris_id, NULL);
rtsp_server->eventLoopWatchVariable = 0;
if(rtsp_server->rtspServer == NULL) {
*rtsp_server->usageEnvironment << "Failed to create rtsp server ::" << rtsp_server->usageEnvironment->getResultMsg() <<"\n";
return false;
}
rtsp_server->sms = ServerMediaSession::createNew(*rtsp_server->usageEnvironment, "iris", "iris", "stream");
rtsp_server->liveSubSession = H264LiveServerMediaSession::createNew(*rtsp_server->usageEnvironment, true);
rtsp_server->sms->addSubsession(rtsp_server->liveSubSession);
rtsp_server->rtspServer->addServerMediaSession(rtsp_server->sms);
rtsp_server->taskSchedular->doEventLoop(&rtsp_server->eventLoopWatchVariable);
I was under the assumption that live555 by default used UDP to transport data to the client from the server, which is what I wanted for it's latency benefits over TCP. However while running the server client I happened to check netstat and I found this:
~# netstat | grep 8554
tcp 0 0 x.x.x.x:8554 wsip-x-x-x-x:39224 ESTABLISHED
It is however showing that the communications are going through TCP not UDP. I am a bit confused here, am I mis-interpreting netstat here?
Is there anything I need to tune in my c++ code to force the communication to go through UDP not TCP?
Okay so I figured out the answer. To help anyone else who is curious about this, the code is actually all correct. There is also no mis-interpretation of netstat. RTSP does indeed run over TCP not UDP. However the transport method of the A/V data runs on RTP, a connection that RTSP simply negotiates and instantiates. RTP almost always will run over UDP. To figure out what port and protocol the A/V data stream is going over you will need to sniff the packets sent out via RTSP. In my case the A/V data stream was indeed still going over UDP.
I'm astonished by the lack of documentation on miniupnp, I believe there's a lot of people using it, but almost no documentation at all, I found a piece of code in the source of RakNet to guide me.
Now I'm having a conceptual issue...
I'm developing an app that connects to a server via UDP (the server should be accessible, the server UDP port is a specific one which is open, and I can test this using any open port checker), then the server puts two or more clients talking to each other (p2p), so I need to circumvent NAT in the clients for that to work.
I already have NAT punch through working, and that already solves lots of cases.
Now, I want to add the UPNP functionality, to attack the NAT issue with this too.
I'm using miniupnpc, and I handle the connections with Boost.Asio.
struct UPNPDev * devlist = 0;
devlist = upnpDiscover(2000, 0, 0, 0, 0, 0);
if (devlist) {
std::cout << "\nList of UPNP devices found on the network :\n";
struct UPNPDev * device;
for(device = devlist; device; device = device->pNext) {
std::cout << "\ndesc: " << device->descURL << "\n st: " << device->st << "\n";
}
char lanaddr[64]; /* my ip address on the LAN */
struct UPNPUrls urls;
struct IGDdatas data;
if (UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)) == 1) {
string port = lexical_cast<string>(socket->local_endpoint().port());
int r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
port.c_str(), port.c_str(), lanaddr, 0, "UDP", 0, "0");
if (r != UPNPCOMMAND_SUCCESS) {
std::cout << "\nupnp fail";
}
char intPort[6];
char intClient[16];
char desc[128];
char enabled[128];
char leaseDuration[128];
r = UPNP_GetSpecificPortMappingEntry(urls.controlURL,
data.first.servicetype,
port.c_str(), "UDP", 0,
intClient, intPort,
desc, enabled, leaseDuration);
if (r != UPNPCOMMAND_SUCCESS) {
std::cout << "\nupnp fail";
}else {
std::cout << "\nupnp success on port " << port;
}
}
}
As you can see, I execute the UPNP after having bound the socket (I bind it without an specific port, like this:)
asio::udp::socket socket(*this->ioService, asio::udp::endpoint());
My question is, does this UPNP execution makes any sense? Will this socket actually use the UPNP port map if I execute the UPNP on the random port bound to the socket AFTER I bind it?
Thanks guys!
i try to do basic ftp client and I have got problem. I succesfully send user and password with correct response, bud then if I want to send others command, it send me back "500 unknown command" everytime..
What can I do wrong?
Code:
string message = "PWD";
message.append("\n");
cout << message;
if((send(mysocket,message.c_str(),message.size() + 1, 0 )) < 0){
cerr << "Error" << endl;
return -1;
}
There is no PWD command in FTP, its called CWD. See RFC959 for the FTP commands.
I am uncertain about a few things regarding ftp file transfer. I am writing an ftp server and I am trying to figure out how to make the file tranfer work correctly. So far it works somehow but I have certain doubts. Here is my file transfer function (only retrieve so far):
void RETRCommand(int & clie_sock, int & c_data_sock, char buffer[]){
ifstream file; //clie_sock is used for commands and c_data_sock for data transfer
char *file_name, packet[PACKET_SIZE]; //packet size is 2040
int packet_len, pre_pos = 0, file_end;
file_name = new char[strlen(buffer + 5)];
strcpy(file_name, buffer + 5);
sprintf(buffer, "150 Opening BINARY mode data connection for file transfer\r\n");
if (send(clie_sock, buffer, strlen(buffer), 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent: " << buffer << endl;
file_name[strlen(file_name) - 2] = '\0';
file.open(file_name, ios::in | ios::binary);
if (file.is_open()) {
file.seekg(0, file.end);
file_end = (int) file.tellg();
file.seekg(0, file.beg);
while(file.good()){
pre_pos = file.tellg();
file.read(packet, PACKET_SIZE);
if ((int) file.tellg() == -1)
packet_len = file_end - pre_pos;
else
packet_len = PACKET_SIZE;
if (send(c_data_sock, packet, packet_len, 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent some data" << endl;
}
}
else {
sprintf(buffer, "550 Requested action not taken. File unavailable\r\n", packet);
if (send(clie_sock, buffer, packet_len + 2, 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent: " << buffer << endl;
delete(file_name);
return;
}
sprintf(buffer, "226 Transfer complete\r\n");
if (send(clie_sock, buffer, strlen(buffer), 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent: " << buffer << endl;
close(c_data_sock);
delete(file_name);
}
So one problem is the data transfer itself. I am not exactly sure how it is supposed to work. Now it works like this: the server sends all the data to c_data_sock, closes this socket and then the client starts doing something. Shouldn't the client recieve the data while the server is sending them? And the other problem is the abor command. How am I supposed to recieve the abor command? I tried recv with flag set to MSG_OOB but then I get an error saying "Invalid argument". I would be glad if someone could give me a hint or an example of how to do it right as I don't seem to be able to figure it out myself.
Thanks,
John
Ftp use two connections. First - is command connection, in your case it is clie_sock. 'ABOR' command should be received though it. You going to receive it the same way you received 'RETR' command.
To receive file client establishes data connection with your server ( c_data_sock socket ). It will not be opened till client connects, so this is the answer to your second question. You cannot start client after server executes this function. First client sends 'retr' command to your command socket. Then your sever waits new connection from client ( after sending him data ip and port ). Then client connects ( now you have your c_data_sock ready ) and sends all the data to that socket, which are in turn received by the client.
You probably need to read more about networking in general if you feel you don't understand it. I prefer this one: http://beej.us/guide/bgnet/
Also you have a memory leak here, after you allocate an array with the
file_name = new char[strlen(buffer + 5)];
you need to delete it using
delete [] file_name;
Otherwise file_name will be treated as a simple pointer, not an array, so most of array memory will be kept by your application which is bad especially when creating server.