Source Code (Use browser search to find items of interest.)
Class Index
ktalkd'TalkConnection (./kdenetwork/ktalkd/ktalkd/machines/talkconn.h:41)
class TalkConnection
{
public:
/** Global initialization. To be called once. */
static void init();
/** Create a talk connection.
* @param r_addr Remote machine IP address
* @param r_name Remote user name
* @param l_name Local user name
* @param _protocol Caller's protocol */
TalkConnection(struct in_addr r_addr,
char * r_name,
char * l_name,
ProtocolType _protocol);
/** Destructor. Closes the sockets if opened.*/
~TalkConnection();
/** Create the sockets */
void open_sockets();
/** Close the sockets */
void close_sockets();
/** Methods for talking to remote daemon */
void ctl_transact(int type, int id_num);
int look_for_invite(int mandatory);
/** Connect with address given back in response to LOOK_UP. */
int connect();
/** Prepare to accept a connection from another talk client */
void listen();
/** Accept a connection from another talk client */
int accept();
/** Exchange the first 3 characters, which are edit characters */
void set_edit_chars();
/** Write data into the socket, by 16 char blocks. */
void write_banner(char * banner);
// Methods to retrieve some information
/** Returns the erase char used by the caller. */
char get_char_erase() { return char_erase; }
/** Returns the caller's name. */
char * get_caller_name() { return new_msg.r_name; } // idem in old_msg
/** Returns socket, for reading or writing */
int get_sockt() { return sockt; }
/** Returns response answer and id_num. Allows protocol independence.
* Each param can be null (0L). Then it isn't filled.*/
void getResponseItems(char * answer, int * id_num, struct sockaddr * addr);
/** Returns connection socket address. For FWT. */
const struct sockaddr get_addr() { return new_msg.addr; } // idem in old_msg
// Methods to cheat with this talk connection
// Used by the forwarding machine
void set_addr(const struct sockaddr * addr);
void set_ctl_addr(const struct sockaddr * ctl_addr);
/** Prints the system error message in the log and exits the current thread */
static void p_error(const char * str);
protected:
/** Used by open_sockets. */
int open_socket (struct sockaddr_in *addr, int type);
/** Check remote protocol. Used by ctl_transact. */
void findProtocol();
/** Find the correct IP address that the daemon at host
"destination" has to respond to */
static struct in_addr getReplyAddr (struct in_addr destination);
static struct in_addr defaultReplyAddr;
static short int talkDaemonPort; // Port number of talk demon (517)
static short int ntalkDaemonPort; // Port number of ntalk demon (518)
ProtocolType protocol;
/* inet addresses of the two machines */
struct in_addr my_machine_addr;
struct in_addr his_machine_addr;
int ctl_sockt;
int sockt;
OLD_CTL_MSG old_msg; // holds interesting data
NEW_CTL_MSG new_msg; // holds interesting data
OLD_CTL_RESPONSE old_resp; // only convenience structure for responses
NEW_CTL_RESPONSE new_resp; // only convenience structure for responses
struct sockaddr lookup_addr; // address returned by LOOKUP. Points to xxx_resp.addr
char char_erase;
};
ktalkd'TalkConnection::TalkConnection() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:76)
TalkConnection::TalkConnection(struct in_addr caller_machine_addr,
char * r_name,
char * local_user,
ProtocolType _protocol) :
protocol(_protocol), his_machine_addr(caller_machine_addr), ctl_sockt(-1), sockt(-1)
{
my_machine_addr = getReplyAddr(his_machine_addr);
new_msg.vers = TALK_VERSION;
new_msg.pid = htonl (getpid ()); // is it necessary ?
*new_msg.r_tty = '\0';
old_msg.pid = htonl (getpid ()); // is it necessary ?
*old_msg.r_tty = '\0';
strncpy(new_msg.l_name,local_user,NEW_NAME_SIZE);
strncpy(new_msg.r_name,r_name,NEW_NAME_SIZE);
strncpy(old_msg.l_name,local_user,OLD_NAME_SIZE);
strncpy(old_msg.r_name,r_name,OLD_NAME_SIZE);
}
ktalkd'TalkConnection::~TalkConnection() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:97)
TalkConnection::~TalkConnection()
{
close_sockets();
}
ktalkd'TalkConnection::init() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:102)
void TalkConnection::init()
{
/* look up the address of the local host */
struct hostent *hp = gethostbyname(Options.hostname);
if (!hp) {
syslog(LOG_ERR, "GetHostByName failed for %s.",Options.hostname);
exit(-1);
}
memcpy(&defaultReplyAddr, hp->h_addr, hp->h_length);
/* find the server's ports */
struct servent * sp = getservbyname("talk", "udp");
if (sp == 0)
syslog(LOG_ERR, "talkconnection: talk/udp: service is not registered.\n");
talkDaemonPort = sp->s_port; // already in network byte order
sp = getservbyname("ntalk", "udp");
if (sp == 0)
syslog(LOG_ERR, "talkconnection: ntalk/udp: service is not registered.\n");
ntalkDaemonPort = sp->s_port; // already in network byte order
}
ktalkd'TalkConnection::open_socket() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:124)
int TalkConnection::open_socket (struct sockaddr_in *addr, int type)
{
addr->sin_family = AF_INET;
addr->sin_addr = my_machine_addr;
addr->sin_port = 0;
int newSocket = socket (PF_INET, type, 0);
if (newSocket <= 0)
p_error ("Unable to open a new socket!");
ksize_t length = sizeof (*addr);
if (bind (newSocket, (struct sockaddr *) addr, length) != 0) {
::close (newSocket);
p_error ("Error binding socket!");
}
if (getsockname (newSocket, (struct sockaddr *) addr, &length) == -1) {
::close (newSocket);
p_error ("New socket has a bad address!");
}
return newSocket;
}
ktalkd'TalkConnection::open_sockets() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:145)
void TalkConnection::open_sockets()
{
struct sockaddr_in ctl_addr;
struct sockaddr_in my_addr;
/* open the ctl socket */
ctl_sockt = open_socket(&ctl_addr, SOCK_DGRAM);
/* store its address */
set_ctl_addr((const struct sockaddr *)&ctl_addr);
/* open the text socket */
sockt = open_socket(&my_addr, SOCK_STREAM);
/* store its address */
set_addr((const struct sockaddr *)&my_addr);
}
/* Tries to find out the correct IP address that the daemon at host
"destination" has to respond to - code borrowed from ktalk, thanks Burkhard ! */
struct in_addr TalkConnection::getReplyAddr (struct in_addr destination) {
in_addr *result;
unsigned char *help1;
unsigned char *help2;
/* disabled caching - I don't have QIntDict ...
result = replyAddrList [(long) destination.s_addr];
if (result) {
return *result;
}
*/
int testsock, i;
result = new (struct in_addr);
struct sockaddr_in client, daemon;
for (i = 0; i < 2; i++) {
client.sin_family = daemon.sin_family = AF_INET;
client.sin_addr.s_addr = htonl (INADDR_ANY);
client.sin_port = htons (0);
daemon.sin_addr = destination;
daemon.sin_port = i ? ntalkDaemonPort : talkDaemonPort;
// Connect to the daemon socket address
// On some UNIXes (such as Linux) this works and sets the IP address queried
// by getsockname to the local machine address used to reach the daemon.
// If it doesn't work (e.g. on SunOS and Solaris), the default machine
// address is used instead.
ksize_t length = sizeof (daemon);
if ((testsock = socket (AF_INET, SOCK_DGRAM, 0)) >= 0 &&
bind (testsock, (struct sockaddr *) &client, sizeof (client)) == 0 &&
::connect (testsock, (struct sockaddr *) &daemon,
sizeof (daemon)) == 0 &&
getsockname (testsock, (struct sockaddr *) &client, &length) != -1 &&
client.sin_addr.s_addr != htonl (INADDR_ANY))
{
*result = client.sin_addr;
message ("Found reply address");
::close (testsock);
break;
}
if (testsock >= 0) ::close (testsock);
}
if (i == 2) {
*result = defaultReplyAddr;
message ("Couldn't find reply address, using default");
}
if (Options.debug_mode) {
help1 = (unsigned char *) &destination;
help2 = (unsigned char *) result;
syslog ( LOG_DEBUG,
"detected reply address for %d.%d.%d.%d: %d.%d.%d.%d",
help1 [0], help1 [1], help1 [2], help1 [3],
help2 [0], help2 [1], help2 [2], help2 [3]);
/* replyAddrList.insert ((long) destination.s_addr, result); disabled */
}
return *result;
}
/* QIntDict <in_addr> TalkConnection::replyAddrList; */
/** Check the remote protocol. Result stored in <protocol>.
* @return 1 if succeeded to find at least 1 protocol */
ktalkd'TalkConnection::findProtocol() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:224)
void TalkConnection::findProtocol() {
message("Remote protocol unknown. Trying to find it. findProtocol()");
/* The existing ctl_sockt will be used for ntalk */
int new_socket = ctl_sockt;
/* We need a new SOCK_DGRAM socket for otalk */
struct sockaddr_in old_ctl_addr;
int old_socket = open_socket(&old_ctl_addr, SOCK_DGRAM);
/* Fill the old_msg return-address to match the address of old_socket */
old_msg.ctl_addr = *(struct sockaddr *)&old_ctl_addr;
old_msg.ctl_addr.sa_family = htons(AF_INET);
/* Prepare two LOOK_UP ctl messages */
old_msg.type = LOOK_UP;
old_msg.id_num = htonl(0L);
new_msg.type = LOOK_UP;
new_msg.id_num = htonl(0L);
char svg_r_name[NEW_NAME_SIZE]; // Save the real r_name
strcpy(svg_r_name, new_msg.r_name);
strcpy(old_msg.r_name, "ktalk");
strcpy(new_msg.r_name, "ktalk");
struct sockaddr_in daemon;
daemon.sin_family = AF_INET;
daemon.sin_addr = his_machine_addr;
/* Prepare the variables used for reading on sockets */
fd_set read_mask, ctl_mask;
int nready=0, cc;
struct timeval wait;
FD_ZERO(&ctl_mask);
FD_SET(new_socket, &ctl_mask);
FD_SET(old_socket, &ctl_mask);
int max_socket = (new_socket > old_socket) ? new_socket : old_socket;
/* Method : we send the two packets to the two remote daemons.
We wait for the first one correct answer, and then we stop everything.
If a wrong answer comes, ignore it (but note that we got it).
If no answer (or two wrong answers), retry, up to 3 times. */
for (int retry = 0; (retry < 3) && (protocol==noProtocol); retry ++)
{
message("Send packets. Retry = %d",retry);
/* Send the messages */
daemon.sin_port = ntalkDaemonPort;
int len = sendto (new_socket, (char *) &new_msg, sizeof new_msg, 0,
(struct sockaddr *) &daemon, sizeof daemon);
if (len != sizeof new_msg)
syslog(LOG_ERR, "findProtocol: sendto() for ntalk failed!");
daemon.sin_port = talkDaemonPort;
len = sendto (old_socket, (char *) &old_msg, sizeof old_msg, 0,
(struct sockaddr *) &daemon, sizeof daemon);
if (len != sizeof old_msg)
syslog(LOG_ERR, "findProtocol: sendto() for otalk failed!");
do {
/* Wait for response */
read_mask = ctl_mask;
wait.tv_sec = CTL_WAIT;
wait.tv_usec = 0;
nready = ::select(max_socket+1, &read_mask, 0, 0, &wait);
if (nready < 0) {
if (errno == EINTR)
continue;
// Timeout. Let's exit this loop and retry sending.
break;
}
if (nready == 0) message("select returned 0 ! ");
/* Analyze response */
if (FD_ISSET(old_socket, &read_mask)) {
message("Reading on old_socket");
cc = ::recv(old_socket, (char *)&old_resp, sizeof (old_resp), 0);
if (cc < 0) {
if (errno == EINTR)
continue;
// Wrong answer (e.g. too short).
} else
if (old_resp.type == LOOK_UP) protocol=talkProtocol; // FOUND
}
if (FD_ISSET(new_socket, &read_mask)) {
message("Reading on new_socket");
cc = ::recv(new_socket, (char *)&new_resp, sizeof (new_resp), 0);
if (cc < 0) {
if (errno == EINTR)
continue;
// Wrong answer (e.g. too short). Note ntalk answered
} else
if ((new_resp.type == LOOK_UP) && (new_resp.vers == TALK_VERSION))
protocol=ntalkProtocol;
}
} while (protocol==noProtocol);
// wait for a time out, or ok.
} // for
/* restore the real r_name */
strncpy(old_msg.r_name, svg_r_name, OLD_NAME_SIZE);
strncpy(new_msg.r_name, svg_r_name, NEW_NAME_SIZE);
/* restore old.ctl_addr */
old_msg.ctl_addr = new_msg.ctl_addr;
::close(old_socket);
message("Exiting findProtocol");
if (protocol==ntalkProtocol) message("Exiting findProtocol with protocol = NTALK");
else if (protocol==talkProtocol) message("Exiting findProtocol with protocol = NTALK");
else p_error("FATAL : no protocol found.");
}
ktalkd'TalkConnection::set_addr() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:334)
void TalkConnection::set_addr(const struct sockaddr * addr)
{
old_msg.addr = *addr;
old_msg.addr.sa_family = htons(AF_INET);
new_msg.addr = *addr;
new_msg.addr.sa_family = htons(AF_INET);
}
ktalkd'TalkConnection::set_ctl_addr() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:342)
void TalkConnection::set_ctl_addr(const struct sockaddr * ctl_addr)
{
old_msg.ctl_addr = *ctl_addr;
old_msg.ctl_addr.sa_family = htons(AF_INET);
new_msg.ctl_addr = *ctl_addr;
new_msg.ctl_addr.sa_family = htons(AF_INET);
}
ktalkd'TalkConnection::close_sockets() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:350)
void TalkConnection::close_sockets()
{
if (sockt!=-1) { close(sockt); sockt = -1; }
if (ctl_sockt!=-1) { close(ctl_sockt); ctl_sockt = -1; }
}
/*
* SOCKDGRAM is unreliable, so we must repeat messages if we have
* not received an acknowledgement within a reasonable amount
* of time
*/
ktalkd'TalkConnection::ctl_transact() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:361)
void TalkConnection::ctl_transact(int type, int id_num)
{
if (protocol == noProtocol)
/** We've been so far, but we still don't know which protocol to use.
* Let's check it, the way ktalk does. */
findProtocol();
fd_set read_mask, ctl_mask;
int nready=0, cc, size, ok=0;
struct timeval wait;
struct sockaddr_in daemon_addr;
char * msg;
if (protocol == talkProtocol) {
old_msg.type = type;
old_msg.id_num = htonl(id_num);
msg = (char *)&old_msg;
size = sizeof old_msg;
} else {
new_msg.type = type;
new_msg.id_num = htonl(id_num);
msg = (char *)&new_msg;
size = sizeof new_msg;
print_request("ctl_transact: ",&new_msg);
}
daemon_addr.sin_family = AF_INET;
daemon_addr.sin_addr = his_machine_addr;
daemon_addr.sin_port = (protocol == talkProtocol) ? talkDaemonPort : ntalkDaemonPort;
FD_ZERO(&ctl_mask);
FD_SET(ctl_sockt, &ctl_mask);
/* Keep sending the message until a response of
* the proper type is obtained.
*/
do {
/* resend message until a response is obtained */
do {
cc = sendto(ctl_sockt, msg, size, 0,
(struct sockaddr *)&daemon_addr,
sizeof (daemon_addr));
if (cc != size) {
if (errno == EINTR)
continue;
p_error("Error on write to talk daemon");
}
read_mask = ctl_mask;
wait.tv_sec = CTL_WAIT;
wait.tv_usec = 0;
nready = ::select(ctl_sockt+1, &read_mask, 0, 0, &wait);
if (nready < 0) {
if (errno == EINTR)
continue;
p_error("Error waiting for daemon response");
}
if (nready == 0) message("select returned 0 ! ");
} while (nready == 0);
/*
* Keep reading while there are queued messages
* (this is not necessary, it just saves extra
* request/acknowledgements being sent)
*/
do {
if (protocol == talkProtocol)
cc = ::recv(ctl_sockt, (char *)&old_resp, sizeof (old_resp), 0);
else
cc = ::recv(ctl_sockt, (char *)&new_resp, sizeof (new_resp), 0);
if (cc < 0) {
if (errno == EINTR)
continue;
p_error("Error on read from talk daemon");
}
read_mask = ctl_mask;
/* an immediate poll */
timerclear(&wait);
nready = ::select(ctl_sockt+1, &read_mask, 0, 0, &wait);
if (protocol == talkProtocol) ok = (old_resp.type == type);
else ok = ((new_resp.type == type) && (new_resp.vers == TALK_VERSION));
} while (nready > 0 && (!ok));
} while (!ok);
if (protocol == talkProtocol) {
old_resp.id_num = ntohl(old_resp.id_num);
old_resp.addr.sa_family = ntohs(old_resp.addr.sa_family);
} else {
new_resp.id_num = ntohl(new_resp.id_num);
new_resp.addr.sa_family = ntohs(new_resp.addr.sa_family);
}
}
/** Look for an invitation on remote machine */
ktalkd'TalkConnection::look_for_invite() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:452)
int TalkConnection::look_for_invite(int mandatory)
{
/* Check for invitation on caller's machine */
ctl_transact(LOOK_UP, 0);
char answer;
int id_num;
getResponseItems(&answer, &id_num, &lookup_addr);
if (!mandatory) return 0;
/* the switch is for later options, such as multiple invitations */
switch (answer) {
case SUCCESS:
new_msg.id_num = htonl(id_num);
old_msg.id_num = htonl(id_num);
message("TalkConnection::look_for_invite : got SUCCESS");
if (lookup_addr.sa_family != AF_INET)
p_error("Response uses invalid network address");
return (1);
default:
/* there wasn't an invitation waiting for us */
message("TalkConnection::look_for_invite : didn't get SUCCESS");
return (0);
}
}
/** Prepare to accept a connection from another talk client */
ktalkd'TalkConnection::listen() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:482)
void TalkConnection::listen()
{
if (::listen(sockt, SOMAXCONN) != 0)
p_error("Error on attempt to listen for caller");
}
/** Accept a connection from another talk client */
ktalkd'TalkConnection::accept() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:489)
int TalkConnection::accept()
{
int accept_sockt;
while ((accept_sockt = ::accept(sockt, 0, 0)) < 0) {
if (errno == EINTR)
continue;
p_error("Unable to connect with your party");
}
::close(sockt);
sockt = accept_sockt;
return sockt;
}
/** Connect to another talk client. */
ktalkd'TalkConnection::connect() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:503)
int TalkConnection::connect()
{
message("Waiting to connect");
do {
errno = 0;
if (::connect(sockt, &lookup_addr, sizeof (struct sockaddr)) != -1)
return 1;
} while (errno == EINTR);
if (errno == ECONNREFUSED) {
/*
* The caller gave up, but his invitation somehow
* was not cleared. Clear it and initiate an
* invitation. (We know there are no newer invitations,
* the talkd works LIFO.)
*/
message("ECONNREFUSED");
ctl_transact(DELETE, 0);
close_sockets();
return 0;
}
p_error("Unable to connect with initiator");
/*NOTREACHED*/
return 0;
}
/** Trade edit characters with the other talk. By agreement
* the first three characters each talk transmits after
* connection are the three edit characters.
* A normal talk client uses tcgetattr() to get the chars,
* but the daemon isn't connected to a terminal, so we can't call it.
* We just send dummy chars, to disable control chars. */
ktalkd'TalkConnection::set_edit_chars() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:534)
void TalkConnection::set_edit_chars()
{
char buf[3];
int cc;
buf[0] = buf[1] = buf[2] = (char)0xff;
/* Write our config to the caller */
cc = write(sockt, buf, sizeof(buf));
if (cc != sizeof(buf) )
p_error("Lost the connection");
/* Read the caller configuration */
cc = read(sockt, buf, sizeof(buf));
if (cc != sizeof(buf) )
p_error("Lost the connection");
char_erase = buf[0]; // store it in TalkConnection
}
ktalkd'TalkConnection::write_banner() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:550)
void TalkConnection::write_banner(char * banner)
{ /* writes the message 'banner', null-terminated */
int count = strlen(banner);
int nbsent;
char * str = banner;
/* message_d("Count : %d.",count); */
while (count>0) {
/* let's send 16 -bytes-max packets */
if (count>=16) nbsent = write(sockt,str,16);
else nbsent = write(sockt,str,count);
count -= nbsent;
str += nbsent;
fsync(sockt);
}
write(sockt,"\n",1);
}
ktalkd'TalkConnection::getResponseItems() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:567)
void TalkConnection::getResponseItems(char * answer, int * id_num, struct sockaddr * addr) {
if (protocol == talkProtocol) {
if (answer) *answer = old_resp.answer;
if (id_num) *id_num = old_resp.id_num;
if (addr) *addr = old_resp.addr;
} else {
if (answer) *answer = new_resp.answer;
if (id_num) *id_num = new_resp.id_num;
if (addr) *addr = new_resp.addr;
}
}
/** p_error prints the system error message in the log
* and then exits. */
ktalkd'TalkConnection::p_error() (./kdenetwork/ktalkd/ktalkd/machines/talkconn.cpp:581)
void TalkConnection::p_error(const char *str)
{
syslog(LOG_ERR,str);
_exit(0);
}