/** * xrdp: A Remote Desktop Protocol server. * * Copyright (C) Jay Sorg 2012-2013 * Copyright (C) Laxmikant Rashinkar 2012-2013 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #if defined(HAVE_CONFIG_H) #include #endif #define LOG_LEVEL 1 #define LLOG(_level, _args) \ do { if (_level < LOG_LEVEL) { ErrorF _args ; } } while (0) #define LLOGLN(_level, _args) \ do { if (_level < LOG_LEVEL) { printf _args ; printf("\n"); } } while (0) #include #include #include #include #include #include #include #include #include #include #include #include "xrdp_sockets.h" #include "xrdpapi.h" struct wts_obj { int fd; int display_num; }; /* helper functions used by WTSxxx API - do not invoke directly */ static int get_display_num_from_display(char *display_text); static int can_send(int sck, int millis); static int can_recv(int sck, int millis); static int mysend(int sck, const void* adata, int bytes); static int myrecv(int sck, void* adata, int bytes); /* * Opens a handle to the server end of a specified virtual channel - this * call is deprecated - use WTSVirtualChannelOpenEx() instead * * @param hServer * @param SessionId - current session ID; *must* be WTS_CURRENT_SERVER_HANDLE * @param pVirtualName - virtual channel name when using SVC * - name of endpoint listener when using DVC * * @return a valid pointer on success, NULL on error ******************************************************************************/ void * WTSVirtualChannelOpen(void *hServer, unsigned int SessionId, const char *pVirtualName) { if (hServer != WTS_CURRENT_SERVER_HANDLE) { return 0; } return WTSVirtualChannelOpenEx(SessionId, pVirtualName, 0); } /* * Opens a handle to the server end of a specified virtual channel * * @param SessionId - current session ID; *must* be WTS_CURRENT_SERVER_HANDLE * @param pVirtualName - virtual channel name when using SVC * - name of endpoint listener when using DVC * @param flags - type of channel and channel priority if DVC * * @return a valid pointer on success, NULL on error ******************************************************************************/ void * WTSVirtualChannelOpenEx(unsigned int SessionId, const char *pVirtualName, unsigned int flags) { struct wts_obj *wts; char *display_text; int bytes; unsigned long long1; struct sockaddr_un s; char *connect_data; int chan_name_bytes; int lerrno; if (SessionId != WTS_CURRENT_SESSION) { LLOGLN(0, ("WTSVirtualChannelOpenEx: bad SessionId")); return 0; } wts = (struct wts_obj *) calloc(1, sizeof(struct wts_obj)); if (wts == NULL) { LLOGLN(0, ("WTSVirtualChannelOpenEx: calloc failed")); return 0; } wts->fd = -1; display_text = getenv("DISPLAY"); if (display_text != 0) { wts->display_num = get_display_num_from_display(display_text); } if (wts->display_num <= 0) { LLOGLN(0, ("WTSVirtualChannelOpenEx: fatal error; display is 0")); free(wts); return NULL; } /* we use unix domain socket to communicate with chansrv */ if ((wts->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { LLOGLN(0, ("WTSVirtualChannelOpenEx: socket failed")); free(wts); return NULL; } /* set non blocking */ long1 = fcntl(wts->fd, F_GETFL); long1 = long1 | O_NONBLOCK; if (fcntl(wts->fd, F_SETFL, long1) < 0) { LLOGLN(0, ("WTSVirtualChannelOpenEx: set non-block mode failed")); } /* connect to chansrv session */ memset(&s, 0, sizeof(struct sockaddr_un)); s.sun_family = AF_UNIX; bytes = sizeof(s.sun_path); snprintf(s.sun_path, bytes - 1, CHANSRV_API_STR, wts->display_num); s.sun_path[bytes - 1] = 0; bytes = sizeof(struct sockaddr_un); if (connect(wts->fd, (struct sockaddr *) &s, bytes) < 0) { lerrno = errno; if ((lerrno == EWOULDBLOCK) || (lerrno == EAGAIN) || (lerrno == EINPROGRESS)) { /* ok */ } else { LLOGLN(0, ("WTSVirtualChannelOpenEx: connect failed")); free(wts); return NULL; } } /* wait for connection to complete */ if (!can_send(wts->fd, 500)) { LLOGLN(0, ("WTSVirtualChannelOpenEx: can_send failed")); free(wts); return NULL; } chan_name_bytes = strlen(pVirtualName); bytes = 4 + 4 + 4 + chan_name_bytes + 4; LLOGLN(10, ("WTSVirtualChannelOpenEx: chan_name_bytes %d bytes %d pVirtualName %s", chan_name_bytes, bytes, pVirtualName)); connect_data = (char *) calloc(bytes, 1); if (connect_data == NULL) { LLOGLN(0, ("WTSVirtualChannelOpenEx: calloc failed")); free(wts); return NULL; } connect_data[0] = (bytes >> 0) & 0xFF; connect_data[1] = (bytes >> 8) & 0xFF; connect_data[2] = (bytes >> 16) & 0xFF; connect_data[3] = (bytes >> 24) & 0xFF; /* version here(4-7), just leave 0 */ connect_data[8] = (chan_name_bytes >> 0) & 0xFF; connect_data[9] = (chan_name_bytes >> 8) & 0xFF; connect_data[10] = (chan_name_bytes >> 16) & 0xFF; connect_data[11] = (chan_name_bytes >> 24) & 0xFF; memcpy(connect_data + 12, pVirtualName, chan_name_bytes); connect_data[4 + 4 + 4 + chan_name_bytes + 0] = (flags >> 0) & 0xFF; connect_data[4 + 4 + 4 + chan_name_bytes + 1] = (flags >> 8) & 0xFF; connect_data[4 + 4 + 4 + chan_name_bytes + 2] = (flags >> 16) & 0xFF; connect_data[4 + 4 + 4 + chan_name_bytes + 3] = (flags >> 24) & 0xFF; LLOGLN(10, ("WTSVirtualChannelOpenEx: calling mysend with %d bytes", bytes)); if (mysend(wts->fd, connect_data, bytes) != bytes) { LLOGLN(0, ("WTSVirtualChannelOpenEx: mysend failed")); free(wts); return NULL; } LLOGLN(10, ("WTSVirtualChannelOpenEx: sent ok")); if (!can_recv(wts->fd, 500)) { LLOGLN(0, ("WTSVirtualChannelOpenEx: can_recv failed")); free(wts); return NULL; } /* get response */ if (myrecv(wts->fd, connect_data, 4) != 4) { LLOGLN(0, ("WTSVirtualChannelOpenEx: myrecv failed")); free(wts); return NULL; } if ((connect_data[0] != 0) || (connect_data[1] != 0) || (connect_data[2] != 0) || (connect_data[3] != 0)) { LLOGLN(0, ("WTSVirtualChannelOpenEx: connect_data not ok")); free(wts); return NULL; } return wts; } /* * Prevent receiving SIGPIPE on disconnect using either MSG_NOSIGNAL (Linux) * or SO_NOSIGPIPE (Mac OS X) */ #if !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif /*****************************************************************************/ static int mysend(int sck, const void *adata, int bytes) { int sent; int error; const char *data; #if defined(SO_NOSIGPIPE) const int on = 1; setsockopt(sck, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); #endif data = (const char *) adata; sent = 0; while (sent < bytes) { if (can_send(sck, 100)) { error = send(sck, data + sent, bytes - sent, MSG_NOSIGNAL); if (error < 1) { return -1; } sent += error; } } return sent; } /*****************************************************************************/ static int myrecv(int sck, void *adata, int bytes) { int recd; int error; char *data; #if defined(SO_NOSIGPIPE) const int on = 1; setsockopt(sck, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); #endif data = (char *) adata; recd = 0; while (recd < bytes) { if (can_recv(sck, 100)) { error = recv(sck, data + recd, bytes - recd, MSG_NOSIGNAL); if (error < 1) { return -1; } recd += error; } } return recd; } /* * write data to client connection * * @return 1 on success, 0 on error *****************************************************************************/ int WTSVirtualChannelWrite(void *hChannelHandle, const char *Buffer, unsigned int Length, unsigned int *pBytesWritten) { struct wts_obj *wts; int rv; wts = (struct wts_obj *) hChannelHandle; *pBytesWritten = 0; if (wts == 0) { LLOGLN(10, ("WTSVirtualChannelWrite: wts is NULL")); return 0; } if (!can_send(wts->fd, 0)) { return 1; /* can't write now, ok to try again */ } rv = mysend(wts->fd, Buffer, Length); LLOGLN(10, ("WTSVirtualChannelWrite: mysend() returned %d", rv)); if (rv >= 0) { /* success, but zero bytes may have been written */ *pBytesWritten = rv; return 1; } /* fatal error */ return 0; } /* * read data from a client connection * * @return 1 on success, 0 on error *****************************************************************************/ int WTSVirtualChannelRead(void *hChannelHandle, unsigned int TimeOut, char *Buffer, unsigned int BufferSize, unsigned int *pBytesRead) { struct wts_obj *wts; int rv; int lerrno; wts = (struct wts_obj *)hChannelHandle; if (wts == 0) { return 0; } if (can_recv(wts->fd, TimeOut)) { rv = recv(wts->fd, Buffer, BufferSize, 0); if (rv == -1) { lerrno = errno; if ((lerrno == EWOULDBLOCK) || (lerrno == EAGAIN) || (lerrno == EINPROGRESS)) { *pBytesRead = 0; return 1; } return 0; } else if (rv == 0) { return 0; } else if (rv > 0) { *pBytesRead = rv; return 1; } } *pBytesRead = 0; return 1; } /*****************************************************************************/ int WTSVirtualChannelClose(void *hChannelHandle) { struct wts_obj *wts; wts = (struct wts_obj *)hChannelHandle; if (wts == NULL) { return 0; } if (wts->fd != -1) { close(wts->fd); } free(wts); return 1; } /*****************************************************************************/ int WTSVirtualChannelQuery(void *hChannelHandle, WTS_VIRTUAL_CLASS WtsVirtualClass, void **ppBuffer, unsigned int *pBytesReturned) { struct wts_obj *wts; wts = (struct wts_obj *)hChannelHandle; if (wts == NULL) { return 0; } if (WtsVirtualClass == WTSVirtualFileHandle) { *pBytesReturned = 4; *ppBuffer = malloc(4); if (*ppBuffer == NULL) { return 0; } memcpy(*ppBuffer, &(wts->fd), 4); } return 1; } /*****************************************************************************/ void WTSFreeMemory(void *pMemory) { if (pMemory != NULL) { free(pMemory); } } /***************************************************************************** ** ** ** ** ** Helper functions used by WTSxxx API - do not invoke directly ** ** ** ** ** *****************************************************************************/ /* * check if socket is in a writable state - i.e will not block on write * * @param sck socket to check * @param millis timeout value in milliseconds * * @return 0 if write will block * @return 1 if write will not block ******************************************************************************/ static int can_send(int sck, int millis) { struct timeval time; fd_set wfds; int select_rv; /* setup for a select call */ FD_ZERO(&wfds); FD_SET(sck, &wfds); time.tv_sec = millis / 1000; time.tv_usec = (millis * 1000) % 1000000; /* check if it is ok to write to specified socket */ select_rv = select(sck + 1, 0, &wfds, 0, &time); return (select_rv > 0) ? 1 : 0; } /*****************************************************************************/ static int can_recv(int sck, int millis) { struct timeval time; fd_set rfds; int select_rv; FD_ZERO(&rfds); FD_SET(sck, &rfds); time.tv_sec = millis / 1000; time.tv_usec = (millis * 1000) % 1000000; select_rv = select(sck + 1, &rfds, 0, 0, &time); if (select_rv > 0) { return 1; } return 0; } /*****************************************************************************/ static int get_display_num_from_display(char *display_text) { int index; int mode; int disp_index; char disp[256]; index = 0; disp_index = 0; mode = 0; while (display_text[index] != 0) { if (display_text[index] == ':') { mode = 1; } else if (display_text[index] == '.') { mode = 2; } else if (mode == 1) { disp[disp_index] = display_text[index]; disp_index++; } index++; } disp[disp_index] = 0; return atoi(disp); }