redirection of disk, lptport, printer, comport.

git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/trunk/rdesktop@568 423420c4-83ab-492f-b58f-81f9feb106b5
This commit is contained in:
Peter Kallden 2004-01-21 14:40:40 +00:00
parent f76ace37f2
commit a61eac7b19
13 changed files with 2237 additions and 257 deletions

View File

@ -15,7 +15,7 @@ datadir = $(prefix)/share/rdesktop
VERSION = 1.3.1 VERSION = 1.3.1
KEYMAP_PATH = $(datadir)/keymaps/ KEYMAP_PATH = $(datadir)/keymaps/
RDPOBJ = tcp.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o RDPOBJ = tcp.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o
X11OBJ = rdesktop.o xwin.o xkeymap.o ewmhints.o xclip.o cliprdr.o X11OBJ = rdesktop.o xwin.o xkeymap.o ewmhints.o xclip.o cliprdr.o
VNCOBJ = vnc/rdp2vnc.o vnc/vnc.o vnc/xkeymap.o vnc/x11stubs.o VNCOBJ = vnc/rdp2vnc.o vnc/vnc.o vnc/xkeymap.o vnc/x11stubs.o
CRYPTOBJ = crypto/rc4_enc.o crypto/rc4_skey.o crypto/md5_dgst.o crypto/sha1dgst.o crypto/bn_exp.o crypto/bn_mul.o crypto/bn_div.o crypto/bn_sqr.o crypto/bn_add.o crypto/bn_shift.o crypto/bn_asm.o crypto/bn_ctx.o crypto/bn_lib.o CRYPTOBJ = crypto/rc4_enc.o crypto/rc4_skey.o crypto/md5_dgst.o crypto/sha1dgst.o crypto/bn_exp.o crypto/bn_mul.o crypto/bn_div.o crypto/bn_sqr.o crypto/bn_add.o crypto/bn_shift.o crypto/bn_asm.o crypto/bn_ctx.o crypto/bn_lib.o

View File

@ -7,7 +7,7 @@
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@ -317,3 +317,20 @@ enum RDP_INPUT_DEVICE
#define STATUS_INVALID_PARAMETER 0xc000000d #define STATUS_INVALID_PARAMETER 0xc000000d
#define STATUS_INVALID_DEVICE_REQUEST 0xc0000010 #define STATUS_INVALID_DEVICE_REQUEST 0xc0000010
#define STATUS_ACCESS_DENIED 0xc0000022 #define STATUS_ACCESS_DENIED 0xc0000022
#define STATUS_NO_SUCH_FILE 0xc000000f
#define STATUS_NO_MORE_FILES 0x80000006
#define STATUS_INVALID_HANDLE 0xc0000008
#define STATUS_NOT_SUPPORTED 0xc00000bb
#define STATUS_PENDING 0x00000103
#define STATUS_CANCELLED 0xc0000120
#define STATUS_TIMEOUT 0xc0000102
/* RDPDR constants */
#define RDPDR_MAX_DEVICES 0x10
#define DEVICE_TYPE_SERIAL 0x01
#define DEVICE_TYPE_PARALLEL 0x02
#define DEVICE_TYPE_PRINTER 0x04
#define DEVICE_TYPE_DISK 0x08
#define DEVICE_TYPE_SCARD 0x20
#define FILE_DIRECTORY_FILE 0x00000001

630
disk.c Normal file
View File

@ -0,0 +1,630 @@
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Disk Redirection
Copyright (C) Jeroen Meijer 2003
This program 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 2 of the License, or
(at your option) any later version.
This program 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 program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define FILE_ATTRIBUTE_READONLY 0x00000001
#define FILE_ATTRIBUTE_HIDDEN 0x00000002
#define FILE_ATTRIBUTE_SYSTEM 0x00000004
#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
#define FILE_ATTRIBUTE_ARCHIVE 0x00000020
#define FILE_ATTRIBUTE_DEVICE 0x00000040
#define FILE_ATTRIBUTE_NORMAL 0x00000080
#define FILE_ATTRIBUTE_TEMPORARY 0x00000100
#define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200
#define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400
#define FILE_ATTRIBUTE_COMPRESSED 0x00000800
#define FILE_ATTRIBUTE_OFFLINE 0x00001000
#define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000
#define FILE_ATTRIBUTE_ENCRYPTED 0x00004000
#define FILE_BASIC_INFORMATION 0x04
#define FILE_STANDARD_INFORMATION 0x05
#define FS_CASE_SENSITIVE 0x00000001
#define FS_CASE_IS_PRESERVED 0x00000002
#define FS_UNICODE_STORED_ON_DISK 0x00000004
#define FS_PERSISTENT_ACLS 0x00000008
#define FS_FILE_COMPRESSION 0x00000010
#define FS_VOLUME_QUOTAS 0x00000020
#define FS_SUPPORTS_SPARSE_FILES 0x00000040
#define FS_SUPPORTS_REPARSE_POINTS 0x00000080
#define FS_SUPPORTS_REMOTE_STORAGE 0X00000100
#define FS_VOL_IS_COMPRESSED 0x00008000
#define FILE_READ_ONLY_VOLUME 0x00080000
#define OPEN_EXISTING 1
#define CREATE_NEW 2
#define OPEN_ALWAYS 3
#define TRUNCATE_EXISTING 4
#define CREATE_ALWAYS 5
#define GENERIC_READ 0x80000000
#define GENERIC_WRITE 0x40000000
#define GENERIC_EXECUTE 0x20000000
#define GENERIC_ALL 0x10000000
#define ERROR_FILE_NOT_FOUND 2L
#define ERROR_ALREADY_EXISTS 183L
#define MAX_OPEN_FILES 0x100
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vfs.h> /* linux statfs */
#include <unistd.h>
#include <fcntl.h> /* open, close */
#include <dirent.h> /* opendir, closedir, readdir */
#include <fnmatch.h>
#include <errno.h> /* errno */
#include "rdesktop.h"
extern RDPDR_DEVICE g_rdpdr_device[];
struct fileinfo
{
uint32 device_id, flags_and_attributes;
char path[256];
DIR *pdir;
struct dirent *pdirent;
char pattern[64];
BOOL delete_on_close;
}
g_fileinfo[MAX_OPEN_FILES];
/* Convert seconds since 1970 to a filetime */
void
seconds_since_1970_to_filetime(time_t seconds, uint32 * high, uint32 * low)
{
unsigned long long ticks;
ticks = (seconds + 11644473600LL) * 10000000;
*low = (uint32) ticks;
*high = (uint32) (ticks >> 32);
}
/* Enumeration of devices from rdesktop.c */
/* returns numer of units found and initialized. */
/* optarg looks like ':h:=/mnt/floppy,b:=/mnt/usbdevice1' */
/* when it arrives to this function. */
int
disk_enum_devices(int *id, char *optarg)
{
char *pos = optarg;
char *pos2;
int count = 0;
// skip the first colon
optarg++;
while ((pos = next_arg(optarg, ',')) && *id < RDPDR_MAX_DEVICES)
{
pos2 = next_arg(optarg, '=');
strcpy(g_rdpdr_device[*id].name, optarg);
toupper(g_rdpdr_device[*id].name);
/* add trailing colon to name. */
strcat(g_rdpdr_device[*id].name, ":");
g_rdpdr_device[*id].local_path = xmalloc(strlen(pos2) + 1);
strcpy(g_rdpdr_device[*id].local_path, pos2);
printf("DISK %s to %s\n", g_rdpdr_device[*id].name, g_rdpdr_device[*id].local_path);
g_rdpdr_device[*id].device_type = DEVICE_TYPE_DISK;
count++;
(*id)++;
optarg = pos;
}
return count;
}
/* Opens of creates a file or directory */
NTSTATUS
disk_create(uint32 device_id, uint32 accessmask, uint32 sharemode, uint32 create_disposition,
uint32 flags_and_attributes, char *filename, HANDLE * phandle)
{
HANDLE handle;
DIR *dirp;
int flags, mode;
char path[256];
handle = 0;
dirp = NULL;
flags = 0;
mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
if (filename[strlen(filename) - 1] == '/')
filename[strlen(filename) - 1] = 0;
sprintf(path, "%s%s", g_rdpdr_device[device_id].local_path, filename);
//printf("Open: %s\n", path);
switch (create_disposition)
{
case CREATE_ALWAYS:
// Delete existing file/link.
unlink(path);
flags |= O_CREAT;
break;
case CREATE_NEW:
// If the file already exists, then fail.
flags |= O_CREAT | O_EXCL;
break;
case OPEN_ALWAYS:
// Create if not already exists.
flags |= O_CREAT;
break;
case OPEN_EXISTING:
// Default behaviour
break;
case TRUNCATE_EXISTING:
// If the file does not exist, then fail.
flags |= O_TRUNC;
break;
}
if (flags_and_attributes & FILE_DIRECTORY_FILE)
{
if (flags & O_CREAT)
{
mkdir(path, mode);
}
dirp = opendir(path);
if (!dirp)
{
switch (errno)
{
case EACCES:
return STATUS_ACCESS_DENIED;
case ENOENT:
return STATUS_NO_SUCH_FILE;
default:
perror("opendir");
return STATUS_NO_SUCH_FILE;
}
}
handle = dirfd(dirp);
}
else
{
if (accessmask & GENERIC_ALL
|| (accessmask & GENERIC_READ && accessmask & GENERIC_WRITE))
{
flags |= O_RDWR;
}
else if ((accessmask & GENERIC_WRITE) && !(accessmask & GENERIC_READ))
{
flags |= O_WRONLY;
}
else
{
flags |= O_RDONLY;
}
handle = open(path, flags, mode);
if (handle == -1)
{
switch (errno)
{
case EACCES:
return STATUS_ACCESS_DENIED;
case ENOENT:
return STATUS_NO_SUCH_FILE;
default:
perror("open");
return STATUS_NO_SUCH_FILE;
}
}
}
if (handle >= MAX_OPEN_FILES)
{
error("Maximum number of open files (%s) reached. Increase MAX_OPEN_FILES!\n",
handle);
exit(1);
}
if (dirp)
g_fileinfo[handle].pdir = dirp;
g_fileinfo[handle].device_id = device_id;
g_fileinfo[handle].flags_and_attributes = flags_and_attributes;
strncpy(g_fileinfo[handle].path, path, 255);
*phandle = handle;
return STATUS_SUCCESS;
}
NTSTATUS
disk_close(HANDLE handle)
{
struct fileinfo *pfinfo;
pfinfo = &(g_fileinfo[handle]);
if (pfinfo->flags_and_attributes & FILE_DIRECTORY_FILE)
{
closedir(pfinfo->pdir);
//FIXME: Should check exit code
}
else
{
close(handle);
}
return STATUS_SUCCESS;
}
NTSTATUS
disk_read(HANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result)
{
int n;
if (offset)
lseek(handle, offset, SEEK_SET);
n = read(handle, data, length);
if (n < 0)
{
perror("read");
*result = 0;
return STATUS_INVALID_PARAMETER;
}
*result = n;
return STATUS_SUCCESS;
}
NTSTATUS
disk_write(HANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result)
{
int n;
if (offset)
lseek(handle, offset, SEEK_SET);
n = write(handle, data, length);
if (n < 0)
{
perror("write");
*result = 0;
return STATUS_ACCESS_DENIED;
}
*result = n;
return STATUS_SUCCESS;
}
NTSTATUS
disk_query_information(HANDLE handle, uint32 info_class, STREAM out)
{
uint32 file_attributes, ft_high, ft_low;
struct stat filestat;
char *path, *filename;
path = g_fileinfo[handle].path;
// Get information about file
if (fstat(handle, &filestat) != 0)
{
perror("stat");
out_uint8(out, 0);
return STATUS_ACCESS_DENIED;
}
// Set file attributes
file_attributes = 0;
if (S_ISDIR(filestat.st_mode))
{
file_attributes |= FILE_ATTRIBUTE_DIRECTORY;
}
filename = 1 + strrchr(path, '/');
if (filename && filename[0] == '.')
{
file_attributes |= FILE_ATTRIBUTE_HIDDEN;
}
// Return requested data
switch (info_class)
{
case 4: /* FileBasicInformation */
out_uint8s(out, 8); //create_time not available;
seconds_since_1970_to_filetime(filestat.st_atime, &ft_high, &ft_low);
out_uint32_le(out, ft_low); //last_access_time
out_uint32_le(out, ft_high);
seconds_since_1970_to_filetime(filestat.st_mtime, &ft_high, &ft_low);
out_uint32_le(out, ft_low); //last_write_time
out_uint32_le(out, ft_high);
out_uint8s(out, 8); //unknown zero
out_uint32_le(out, file_attributes);
break;
case 5: /* FileStandardInformation */
out_uint32_le(out, filestat.st_size); //Allocation size
out_uint32_le(out, 0);
out_uint32_le(out, filestat.st_size); //End of file
out_uint32_le(out, 0);
out_uint32_le(out, filestat.st_nlink); //Number of links
out_uint8(out, 0); //Delete pending
out_uint8(out, S_ISDIR(filestat.st_mode) ? 1 : 0); //Directory
break;
case 35: /* FileObjectIdInformation */
out_uint32_le(out, file_attributes); /* File Attributes */
out_uint32_le(out, 0); /* Reparse Tag */
break;
default:
unimpl("IRP Query (File) Information class: 0x%x\n", info_class);
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
NTSTATUS
disk_set_information(HANDLE handle, uint32 info_class, STREAM in, STREAM out)
{
uint32 device_id, length, file_attributes, ft_high, ft_low;
char newname[256], fullpath[256];
struct fileinfo *pfinfo;
pfinfo = &(g_fileinfo[handle]);
switch (info_class)
{
case 4: /* FileBasicInformation */
// Probably safe to ignore
break;
case 10: /* FileRenameInformation */
in_uint8s(in, 4); /* Handle of root dir? */
in_uint8s(in, 0x1a); /* unknown */
in_uint32_le(in, length);
if (length && (length / 2) < 256)
{
rdp_in_unistr(in, newname, length);
convert_to_unix_filename(newname);
}
else
{
return STATUS_INVALID_PARAMETER;
}
sprintf(fullpath, "%s%s", g_rdpdr_device[pfinfo->device_id].local_path,
newname);
if (rename(pfinfo->path, fullpath) != 0)
{
perror("rename");
return STATUS_ACCESS_DENIED;
}
break;
case 13: /* FileDispositionInformation */
//unimpl("IRP Set File Information class: FileDispositionInformation\n");
// in_uint32_le(in, delete_on_close);
// disk_close(handle);
unlink(pfinfo->path);
break;
case 19: /* FileAllocationInformation */
unimpl("IRP Set File Information class: FileAllocationInformation\n");
break;
case 20: /* FileEndOfFileInformation */
unimpl("IRP Set File Information class: FileEndOfFileInformation\n");
break;
default:
unimpl("IRP Set File Information class: 0x%x\n", info_class);
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
NTSTATUS
disk_query_volume_information(HANDLE handle, uint32 info_class, STREAM out)
{
char *volume, *fs_type;
struct statfs stat_fs;
struct fileinfo *pfinfo;
pfinfo = &(g_fileinfo[handle]);
volume = "RDESKTOP";
fs_type = "RDPFS";
if (statfs(pfinfo->path, &stat_fs) != 0) /* FIXME: statfs is not portable */
{
perror("statfs");
return STATUS_ACCESS_DENIED;
}
switch (info_class)
{
case 1: /* FileFsVolumeInformation */
out_uint32_le(out, 0); /* volume creation time low */
out_uint32_le(out, 0); /* volume creation time high */
out_uint32_le(out, 0); /* serial */
out_uint32_le(out, 2 * strlen(volume)); /* length of string */
out_uint8(out, 0); /* support objects? */
rdp_out_unistr(out, volume, 2 * strlen(volume) - 2);
break;
case 3: /* FileFsSizeInformation */
out_uint32_le(out, stat_fs.f_blocks); /* Total allocation units low */
out_uint32_le(out, 0); /* Total allocation high units */
out_uint32_le(out, stat_fs.f_bfree); /* Available allocation units */
out_uint32_le(out, 0); /* Available allowcation units */
out_uint32_le(out, stat_fs.f_bsize / 0x200); /* Sectors per allocation unit */
out_uint32_le(out, 0x200); /* Bytes per sector */
break;
case 5: /* FileFsAttributeInformation */
out_uint32_le(out, FS_CASE_SENSITIVE | FS_CASE_IS_PRESERVED); /* fs attributes */
out_uint32_le(out, stat_fs.f_namelen); /* max length of filename */
out_uint32_le(out, 2 * strlen(fs_type)); /* length of fs_type */
rdp_out_unistr(out, fs_type, 2 * strlen(fs_type) - 2);
break;
case 2: /* FileFsLabelInformation */
case 4: /* FileFsDeviceInformation */
case 6: /* FileFsControlInformation */
case 7: /* FileFsFullSizeInformation */
case 8: /* FileFsObjectIdInformation */
case 9: /* FileFsMaximumInformation */
default:
unimpl("IRP Query Volume Information class: 0x%x\n", info_class);
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
NTSTATUS
disk_query_directory(HANDLE handle, uint32 info_class, char *pattern, STREAM out)
{
uint32 file_attributes, ft_low, ft_high;
char *dirname, fullpath[256];
DIR *pdir;
struct dirent *pdirent;
struct stat fstat;
struct fileinfo *pfinfo;
pfinfo = &(g_fileinfo[handle]);
pdir = pfinfo->pdir;
dirname = pfinfo->path;
file_attributes = 0;
switch (info_class)
{
case 3: //FIXME: Why 3?
// If a search pattern is received, remember this pattern, and restart search
if (pattern[0] != 0)
{
strncpy(pfinfo->pattern, 1 + strrchr(pattern, '/'), 64);
rewinddir(pdir);
}
// find next dirent matching pattern
pdirent = readdir(pdir);
while (pdirent && fnmatch(pfinfo->pattern, pdirent->d_name, 0) != 0)
{
pdirent = readdir(pdir);
}
if (pdirent == NULL)
{
return STATUS_NO_MORE_FILES;
}
// Get information for directory entry
sprintf(fullpath, "%s/%s", dirname, pdirent->d_name);
/* JIF
printf("Stat: %s\n", fullpath); */
if (stat(fullpath, &fstat))
{
perror("stat");
out_uint8(out, 0);
return STATUS_ACCESS_DENIED;
}
if (S_ISDIR(fstat.st_mode))
file_attributes |= FILE_ATTRIBUTE_DIRECTORY;
if (pdirent->d_name[0] == '.')
file_attributes |= FILE_ATTRIBUTE_HIDDEN;
// Return requested information
out_uint8s(out, 8); //unknown zero
out_uint8s(out, 8); //create_time not available in posix;
seconds_since_1970_to_filetime(fstat.st_atime, &ft_high, &ft_low);
out_uint32_le(out, ft_low); //last_access_time
out_uint32_le(out, ft_high);
seconds_since_1970_to_filetime(fstat.st_mtime, &ft_high, &ft_low);
out_uint32_le(out, ft_low); //last_write_time
out_uint32_le(out, ft_high);
out_uint8s(out, 8); //unknown zero
out_uint32_le(out, fstat.st_size); //filesize low
out_uint32_le(out, 0); //filesize high
out_uint32_le(out, fstat.st_size); //filesize low
out_uint32_le(out, 0); //filesize high
out_uint32_le(out, file_attributes);
out_uint8(out, 2 * strlen(pdirent->d_name) + 2); //unicode length
out_uint8s(out, 7); //pad?
out_uint8(out, 0); //8.3 file length
out_uint8s(out, 2 * 12); //8.3 unicode length
rdp_out_unistr(out, pdirent->d_name, 2 * strlen(pdirent->d_name));
break;
default:
unimpl("IRP Query Directory sub: 0x%x\n", info_class);
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
DEVICE_FNS disk_fns = {
disk_create,
disk_close,
disk_read,
disk_write,
NULL /* device_control */
};

125
parallel.c Normal file
View File

@ -0,0 +1,125 @@
#define MAX_PARALLEL_DEVICES 1
#define FILE_DEVICE_PARALLEL 0x22
#define IOCTL_PAR_QUERY_RAW_DEVICE_ID 0x0c
#define PARALLELDEV0 "/dev/lp0"
#include "rdesktop.h"
#include <unistd.h>
#include <fcntl.h>
extern RDPDR_DEVICE g_rdpdr_device[];
PARALLEL_DEVICE * get_parallel_data(HANDLE handle)
{
int index;
for (index = 0; index < RDPDR_MAX_DEVICES; index++)
{
if (handle == g_rdpdr_device[index].handle)
return (PARALLEL_DEVICE *) g_rdpdr_device[index].pdevice_data;
}
return NULL;
}
/* Enumeration of devices from rdesktop.c */
/* returns numer of units found and initialized. */
/* optarg looks like ':LPT1=/dev/lp0' */
/* when it arrives to this function. */
int
parallel_enum_devices(int *id, char *optarg)
{
//TODO: Read from configuration file? CUPS?
PARALLEL_DEVICE *ppar_info;
char *pos = optarg;
char *pos2;
int count = 0;
// skip the first colon
optarg++;
while ((pos = next_arg(optarg, ',')) && *id < RDPDR_MAX_DEVICES)
{
ppar_info = (PARALLEL_DEVICE *) xmalloc(sizeof(PARALLEL_DEVICE));
pos2 = next_arg(optarg, '=');
strcpy(g_rdpdr_device[*id].name, optarg);
toupper(g_rdpdr_device[*id].name);
g_rdpdr_device[*id].local_path = xmalloc(strlen(pos2) + 1);
strcpy(g_rdpdr_device[*id].local_path, pos2);
printf("PARALLEL %s to %s\n", optarg, pos2);
// set device type
g_rdpdr_device[*id].device_type = DEVICE_TYPE_PARALLEL;
g_rdpdr_device[*id].pdevice_data = (void *) ppar_info;
count++;
(*id)++;
optarg = pos;
}
return count;
}
static NTSTATUS
parallel_create(uint32 device_id, HANDLE * handle)
{
int parallel_fd;
parallel_fd = open(PARALLELDEV0, O_WRONLY);
if (parallel_fd == -1)
return STATUS_ACCESS_DENIED;
*handle = parallel_fd;
return STATUS_SUCCESS;
}
static NTSTATUS
parallel_close(HANDLE handle)
{
close(handle);
return STATUS_SUCCESS;
}
static NTSTATUS
parallel_write(HANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result)
{
*result = write(handle, data, length);
return STATUS_SUCCESS;
}
static NTSTATUS
parallel_device_control(HANDLE handle, uint32 request, STREAM in, STREAM out)
{
if ((request >> 16) != FILE_DEVICE_PARALLEL)
return STATUS_INVALID_PARAMETER;
/* extract operation */
request >>= 2;
request &= 0xfff;
printf("PARALLEL IOCTL %d: ", request);
switch (request)
{
case IOCTL_PAR_QUERY_RAW_DEVICE_ID:
default:
printf("\n");
unimpl("UNKNOWN IOCTL %d\n", request);
}
return STATUS_SUCCESS;
}
DEVICE_FNS parallel_fns = {
parallel_create,
parallel_close,
NULL,
parallel_write,
parallel_device_control
};

127
printer.c
View File

@ -1,26 +1,139 @@
#define MAX_PRINTERS 5
#include "rdesktop.h" #include "rdesktop.h"
FILE *printer_fp; extern RDPDR_DEVICE g_rdpdr_device[];
PRINTER * get_printer_data(HANDLE handle)
{
int index;
for (index = 0; index < RDPDR_MAX_DEVICES; index++)
{
if (handle == g_rdpdr_device[index].handle)
return (PRINTER *) g_rdpdr_device[index].pdevice_data;
}
return NULL;
}
int
printer_enum_devices(int *id, char *optarg)
{
PRINTER *pprinter_data;
char *pos = optarg;
char *pos2;
int count = 0;
int already = 0;
// we need to know how many printers we've already set up
// supplied from other -r flags than this one.
while (count < *id)
{
if (g_rdpdr_device[count].device_type == DEVICE_TYPE_PRINTER)
already++;
count++;
}
count = 0;
if (*optarg == ':')
optarg++;
while ((pos = next_arg(optarg, ',')) && *id < RDPDR_MAX_DEVICES)
{
pprinter_data = (PRINTER *) xmalloc(sizeof(PRINTER));
strcpy(g_rdpdr_device[*id].name, "PRN");
strcat(g_rdpdr_device[*id].name, ltoa(already + count + 1, 10));
/* first printer is set as default printer */
if ((already + count) == 0)
pprinter_data->default_printer = True;
else
pprinter_data->default_printer = False;
pos2 = next_arg(optarg, ':');
if (*optarg == (char) 0x00)
pprinter_data->printer = "mydeskjet"; /* set default */
else
{
pprinter_data->printer = xmalloc(strlen(optarg) + 1);
strcpy(pprinter_data->printer, optarg);
}
if (!pos2 || (*pos2 == (char) 0x00))
pprinter_data->driver = "HP LaserJet IIIP"; /* no printer driver supplied set default */
else
{
pprinter_data->driver = xmalloc(strlen(pos2) + 1);
strcpy(pprinter_data->driver, pos2);
}
printf("PRINTER %s to %s driver %s\n", g_rdpdr_device[*id].name,
pprinter_data->printer, pprinter_data->driver);
g_rdpdr_device[*id].device_type = DEVICE_TYPE_PRINTER;
g_rdpdr_device[*id].pdevice_data = (void *) pprinter_data;
count++;
(*id)++;
optarg = pos;
}
return count;
}
static NTSTATUS static NTSTATUS
printer_create(HANDLE * handle) printer_create(uint32 device_id, uint32 access, uint32 share_mode, uint32 disposition, uint32 flags,
char *filename, HANDLE * handle)
{ {
printer_fp = popen("lpr", "w"); char cmd[256];
*handle = 0; PRINTER *pprinter_data;
pprinter_data = (PRINTER *) g_rdpdr_device[device_id].pdevice_data;
/* default printer name use default printer queue as well in unix */
if (pprinter_data->printer == "mydeskjet")
{
pprinter_data->printer_fp = popen("lpr", "w");
}
else
{
sprintf(cmd, "lpr -P %s", pprinter_data->printer);
pprinter_data->printer_fp = popen(cmd, "w");
}
g_rdpdr_device[device_id].handle = pprinter_data->printer_fp->_fileno;
*handle = g_rdpdr_device[device_id].handle;
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
static NTSTATUS static NTSTATUS
printer_close(HANDLE handle) printer_close(HANDLE handle)
{ {
pclose(printer_fp); PRINTER *pprinter_data;
pprinter_data = get_printer_data(handle);
g_rdpdr_device[get_device_index(handle)].handle = 0;
pclose(pprinter_data->printer_fp);
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
static NTSTATUS static NTSTATUS
printer_write(HANDLE handle, uint8 * data, uint32 length, uint32 * result) printer_write(HANDLE handle, uint8 * data, uint32 length, uint32 offset, uint32 * result)
{ {
*result = fwrite(data, 1, length, printer_fp); PRINTER *pprinter_data;
pprinter_data = get_printer_data(handle);
*result = length * fwrite(data, length, 1, pprinter_data->printer_fp);
if (ferror(pprinter_data->printer_fp))
{
*result = 0;
return STATUS_INVALID_HANDLE;
}
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }

169
printercache.c Normal file
View File

@ -0,0 +1,169 @@
/* -*- c-basic-offset: 8 -*-
* rdesktop: A Remote Desktop Protocol client.
* Entrypoint and utility functions
* Copyright (C) Matthew Chapman 1999-2003
* Copyright (C) Jeroen Meijer 2003
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* According to the W2K RDP Printer Redirection WhitePaper, a data
* blob is sent to the client after the configuration of the printer
* is changed at the server.
*
* This data blob is saved to the registry. The client returns this
* data blob in a new session with the printer announce data.
* The data is not interpreted by the client.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "rdesktop.h"
BOOL
printercache_mkdir(char *base, char *printer)
{
char *path;
path = (char *) xmalloc(strlen(base) + sizeof("/.rdesktop/rdpdr/") + strlen(printer));
sprintf(path, "%s/.rdesktop", base);
if ((mkdir(path, 0700) == -1) && errno != EEXIST)
{
perror(path);
return False;
}
strcat(path, "/rdpdr");
if ((mkdir(path, 0700) == -1) && errno != EEXIST)
{
perror(path);
return False;
}
strcat(path, "/");
strcat(path, printer);
if ((mkdir(path, 0700) == -1) && errno != EEXIST)
{
perror(path);
return False;
}
xfree(path);
return True;
}
int
printercache_load_blob(char *printer_name, uint8 ** data)
{
char *home, *path;
struct stat st;
int fd, length;
if (printer_name == NULL)
return 0;
home = getenv("HOME");
if (home == NULL)
return 0;
path = (char *) xmalloc(strlen(home) + sizeof("/.rdesktop/rdpdr/") + strlen(printer_name) + sizeof("/AutoPrinterCacheData"));
sprintf(path, "%s/.rdesktop/rdpdr/%s/AutoPrinterCacheData", home, printer_name);
fd = open(path, O_RDONLY);
if (fd == -1)
return 0;
if (fstat(fd, &st))
return 0;
*data = (uint8 *) xmalloc(st.st_size);
length = read(fd, *data, st.st_size);
close(fd);
xfree(path);
return length;
}
void
printercache_save_blob(char *printer_name, uint8 * data, uint32 length)
{
char *home, *path;
int fd;
if (printer_name == NULL)
return;
home = getenv("HOME");
if (home == NULL)
return;
if (!printercache_mkdir(home, printer_name))
return;
path = (char *) xmalloc(strlen(home) + sizeof("/.rdesktop/rdpdr/") + strlen(printer_name) + sizeof("/AutoPrinterCacheData"));
sprintf(path, "%s/.rdesktop/rdpdr/%s/AutoPrinterCacheData", home, printer_name);
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd == -1)
{
perror(path);
return;
}
if (write(fd, data, length) != length)
{
perror(path);
unlink(path);
}
close(fd);
xfree(path);
}
void
printercache_process(STREAM s)
{
uint32 type, printer_length, driver_length, printer_unicode_length, blob_length;
char device_name[9], printer[256], driver[256];
in_uint32_le(s, type);
switch (type)
{
case 2:
in_uint32_le(s, printer_unicode_length);
in_uint32_le(s, blob_length);
if (printer_unicode_length < 2 * 255)
{
rdp_in_unistr(s, printer, printer_unicode_length);
printercache_save_blob(printer, s->p, blob_length);
}
break;
case 1:
// TODO: I think this one just tells us what printer is on LPT? but why?
default:
unimpl("RDPDR Printer Cache Packet Type: %d\n", type);
break;
}
}

25
proto.h
View File

@ -26,6 +26,12 @@ void cliprdr_send_native_format_announce(uint8 * data, uint32 length);
void cliprdr_send_data_request(uint32 format); void cliprdr_send_data_request(uint32 format);
void cliprdr_send_data(uint8 * data, uint32 length); void cliprdr_send_data(uint8 * data, uint32 length);
BOOL cliprdr_init(void); BOOL cliprdr_init(void);
/* disk.c */
NTSTATUS disk_query_information(HANDLE handle, uint32 info_class, STREAM out);
NTSTATUS disk_set_information(HANDLE handle, uint32 info_class, STREAM in, STREAM out);
NTSTATUS disk_query_volume_information(HANDLE handle, uint32 info_class, STREAM out);
NTSTATUS disk_query_directory(HANDLE handle, uint32 info_class, char *pattern, STREAM out);
int disk_enum_devices(int *id, char *optarg);
/* ewmhints.c */ /* ewmhints.c */
int get_current_workarea(uint32 * x, uint32 * y, uint32 * width, uint32 * height); int get_current_workarea(uint32 * x, uint32 * y, uint32 * width, uint32 * height);
/* iso.c */ /* iso.c */
@ -46,7 +52,13 @@ void mcs_disconnect(void);
/* orders.c */ /* orders.c */
void process_orders(STREAM s, uint16 num_orders); void process_orders(STREAM s, uint16 num_orders);
void reset_order_state(void); void reset_order_state(void);
/* parallel.c */
int parallel_enum_devices(int *id, char *optarg);
/* printer.c */ /* printer.c */
int printer_enum_devices(int *id, char *optarg);
/* printercache.c */
int printercache_load_blob(char *printer_name, uint8 ** data);
void printercache_process(STREAM s);
/* rdesktop.c */ /* rdesktop.c */
int main(int argc, char *argv[]); int main(int argc, char *argv[]);
void generate_random(uint8 * random); void generate_random(uint8 * random);
@ -56,13 +68,17 @@ void xfree(void *mem);
void error(char *format, ...); void error(char *format, ...);
void warning(char *format, ...); void warning(char *format, ...);
void unimpl(char *format, ...); void unimpl(char *format, ...);
void hexdump(unsigned char *p, int len); void hexdump(unsigned char *p, unsigned int len);
char *toupper(char* p);
char *ltoa(long N, int base);
int load_licence(unsigned char **data); int load_licence(unsigned char **data);
void save_licence(unsigned char *data, int length); void save_licence(unsigned char *data, int length);
char *next_arg(char *src, char needle);
/* rdp5.c */ /* rdp5.c */
void rdp5_process(STREAM s, BOOL encryption); void rdp5_process(STREAM s, BOOL encryption);
/* rdp.c */ /* rdp.c */
void rdp_out_unistr(STREAM s, char *string, int len); void rdp_out_unistr(STREAM s, char *string, int len);
int rdp_in_unistr(STREAM s, char *string, int uni_len);
void rdp_send_input(uint32 time, uint16 message_type, uint16 device_flags, uint16 param1, void rdp_send_input(uint32 time, uint16 message_type, uint16 device_flags, uint16 param1,
uint16 param2); uint16 param2);
void process_colour_pointer_pdu(STREAM s); void process_colour_pointer_pdu(STREAM s);
@ -75,12 +91,15 @@ BOOL rdp_connect(char *server, uint32 flags, char *domain, char *password, char
char *directory); char *directory);
void rdp_disconnect(void); void rdp_disconnect(void);
/* rdpdr.c */ /* rdpdr.c */
void convert_to_unix_filename(char *filename);
void rdpdr_send_connect(void); void rdpdr_send_connect(void);
void rdpdr_send_name(void); void rdpdr_send_name(void);
void rdpdr_send_available(void); void rdpdr_send_available(void);
void rdpdr_send_completion(uint32 device, uint32 id, uint32 status, uint32 result, uint8 * buffer, void rdpdr_send_completion(uint32 device, uint32 id, uint32 status, uint32 result, uint8 * buffer,
uint32 length); uint32 length);
BOOL rdpdr_init(void); BOOL rdpdr_init();
int get_device_index(HANDLE handle);
BOOL rdpdr_abort_io(uint32 fd, uint32 major, NTSTATUS status);
/* rdpsnd.c */ /* rdpsnd.c */
STREAM rdpsnd_init_packet(uint16 type, uint16 size); STREAM rdpsnd_init_packet(uint16 type, uint16 size);
void rdpsnd_send(STREAM s); void rdpsnd_send(STREAM s);
@ -112,6 +131,8 @@ STREAM sec_recv(void);
BOOL sec_connect(char *server, char *username); BOOL sec_connect(char *server, char *username);
void sec_disconnect(void); void sec_disconnect(void);
/* serial.c */ /* serial.c */
BOOL serial_get_timeout(uint32 handle, uint32 length, uint32 * timeout, uint32 * itv_timeout);
int serial_enum_devices(int *id, char *optarg);
/* tcp.c */ /* tcp.c */
STREAM tcp_init(uint32 maxlen); STREAM tcp_init(uint32 maxlen);
void tcp_send(STREAM s); void tcp_send(STREAM s);

View File

@ -1,7 +1,7 @@
/* -*- c-basic-offset: 8 -*- /* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client. rdesktop: A Remote Desktop Protocol client.
Entrypoint and utility functions Entrypoint and utility functions
Copyright (C) Matthew Chapman 1999-2004 Copyright (C) Matthew Chapman 1999-2003
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@ -73,6 +73,9 @@ extern BOOL g_owncolmap;
BOOL g_rdpsnd = False; BOOL g_rdpsnd = False;
#endif #endif
extern RDPDR_DEVICE g_rdpdr_device[];
extern uint32 g_num_devices;
#ifdef RDP2VNC #ifdef RDP2VNC
extern int rfb_port; extern int rfb_port;
extern int defer_time; extern int defer_time;
@ -111,9 +114,18 @@ usage(char *program)
fprintf(stderr, " -K: keep window manager key bindings\n"); fprintf(stderr, " -K: keep window manager key bindings\n");
fprintf(stderr, " -S: caption button size (single application mode)\n"); fprintf(stderr, " -S: caption button size (single application mode)\n");
fprintf(stderr, " -T: window title\n"); fprintf(stderr, " -T: window title\n");
fprintf(stderr, " -N: enable numlock synchronisation\n"); fprintf(stderr, " -N: enable numlock syncronization\n");
fprintf(stderr, " -a: connection colour depth\n"); fprintf(stderr, " -a: connection colour depth\n");
fprintf(stderr, " -r: enable specified device redirection (currently: sound)\n"); fprintf(stderr, " -r: enable specified device redirection (this flag can be repeated)\n");
fprintf(stderr, " '-r comport:COM1=/dev/ttyS0': enable serial redirection of /dev/ttyS0 to COM1\n");
fprintf(stderr, " or :COM1=/dev/ttyS0:9600,0|1|2,0|2,5|6|7|8:dtr \n");
fprintf(stderr, " '-r disk:A=/mnt/floppy': enable redirection of /mnt/floppy to A:\n");
fprintf(stderr, " or A=/mnt/floppy,D=/mnt/cdrom'\n");
fprintf(stderr, " '-r lptport:LPT1=/dev/lp0': enable parallel redirection of /dev/lp0 to LPT1\n");
fprintf(stderr, " or LPT1=/dev/lp0,LPT2=/dev/lp1\n");
fprintf(stderr, " '-r printer:mydeskjet': enable printer redirection\n");
fprintf(stderr, " or mydeskjet:\"HP Laserjet IIIP\" to enter server driver as well\n");
fprintf(stderr, " '-r sound': enable sound redirection\n");
fprintf(stderr, " -0: attach to console\n"); fprintf(stderr, " -0: attach to console\n");
fprintf(stderr, " -4: use RDP version 4\n"); fprintf(stderr, " -4: use RDP version 4\n");
fprintf(stderr, " -5: use RDP version 5 (default)\n"); fprintf(stderr, " -5: use RDP version 5 (default)\n");
@ -218,6 +230,7 @@ main(int argc, char *argv[])
uint32 flags; uint32 flags;
char *p; char *p;
int c; int c;
int username_option = 0; int username_option = 0;
flags = RDP_LOGON_NORMAL; flags = RDP_LOGON_NORMAL;
@ -225,6 +238,8 @@ main(int argc, char *argv[])
domain[0] = password[0] = shell[0] = directory[0] = 0; domain[0] = password[0] = shell[0] = directory[0] = 0;
strcpy(keymapname, "en-us"); strcpy(keymapname, "en-us");
g_num_devices = 0;
#ifdef RDP2VNC #ifdef RDP2VNC
#define VNCOPT "V:Q:" #define VNCOPT "V:Q:"
#else #else
@ -385,12 +400,36 @@ main(int argc, char *argv[])
break; break;
case 'r': case 'r':
if (!strcmp(optarg, "sound"))
if (strncmp("sound", optarg, 5) == 0)
{
#ifdef WITH_RDPSND #ifdef WITH_RDPSND
g_rdpsnd = True; g_rdpsnd = True;
#else #else
warning("Not compiled with sound support"); warning("Not compiled with sound support");
#endif #endif
}
else if (strncmp("disk", optarg, 4) == 0)
{
/* -r disk:h:=/mnt/floppy */
disk_enum_devices(&g_num_devices, optarg + 4);
}
else if (strncmp("comport", optarg, 7) == 0)
{
serial_enum_devices(&g_num_devices, optarg + 7);
}
else if (strncmp("lptport", optarg, 7) == 0)
{
parallel_enum_devices(&g_num_devices, optarg + 7);
}
else if (strncmp("printer", optarg, 7) == 0)
{
printer_enum_devices(&g_num_devices, optarg + 7);
}
else
{
warning("Unknown -r argument\n\n\tPossible arguments are: comport, disk, lptport, printer, sound\n");
}
break; break;
case '0': case '0':
@ -470,7 +509,7 @@ main(int argc, char *argv[])
if (g_rdpsnd) if (g_rdpsnd)
rdpsnd_init(); rdpsnd_init();
#endif #endif
/* rdpdr_init(); */ rdpdr_init();
if (!rdp_connect(server, flags, domain, password, shell, directory)) if (!rdp_connect(server, flags, domain, password, shell, directory))
return 1; return 1;
@ -660,7 +699,7 @@ unimpl(char *format, ...)
/* produce a hex dump */ /* produce a hex dump */
void void
hexdump(unsigned char *p, int len) hexdump(unsigned char *p, unsigned int len)
{ {
unsigned char *line = p; unsigned char *line = p;
int i, thisline, offset = 0; int i, thisline, offset = 0;
@ -687,6 +726,102 @@ hexdump(unsigned char *p, int len)
} }
} }
/*
input: src is the string we look in for needle
return value: returns next src pointer, for
succesive executions, like in a while loop
if retval is 0, then there are no more args.
pitfalls:
src is modified. 0x00 chars are inserted to
terminate strings.
return val, points on the next val chr after ins
0x00
example usage:
while( (pos = next_arg( optarg, ',')) ){
printf("%s\n",optarg);
optarg=pos;
}
*/
char *
next_arg(char *src, char needle)
{
char *nextval;
// EOS
if (*src == (char) 0x00)
return 0;
// more args available.
nextval = strchr(src, needle);
if (nextval)
{
*nextval = (char) 0x00;
return ++nextval;
}
// no more args after this, jump to EOS
nextval = src + strlen(src);
return nextval;
}
char *
toupper(char* p)
{
while( *p ){
if( (*p >= 'a') && (*p <= 'z') )
*p = *p - 32;
p++;
}
}
/* not all clibs got ltoa */
#define LTOA_BUFSIZE (sizeof(long) * 8 + 1)
char *
ltoa(long N, int base)
{
static char ret[LTOA_BUFSIZE];
register int i = 2;
long uarg;
char *tail, *head = ret, buf[LTOA_BUFSIZE];
if (36 < base || 2 > base)
base = 10;
tail = &buf[LTOA_BUFSIZE - 1];
*tail-- = '\0';
if (10 == base && N < 0L)
{
*head++ = '-';
uarg = -N;
}
else
uarg = N;
if (uarg)
{
for (i = 1; uarg; ++i)
{
register ldiv_t r;
r = ldiv(uarg, base);
*tail-- = (char) (r.rem + ((9L < r.rem) ? ('A' - 10L) : '0'));
uarg = r.quot;
}
}
else
*tail-- = '0';
memcpy(head, ++tail, i);
return ret;
}
int int
load_licence(unsigned char **data) load_licence(unsigned char **data)

View File

@ -49,9 +49,11 @@
#endif #endif
#define STRNCPY(dst,src,n) { strncpy(dst,src,n-1); dst[n-1] = 0; } #define STRNCPY(dst,src,n) { strncpy(dst,src,n-1); dst[n-1] = 0; }
#ifndef MIN #ifndef MIN
#define MIN(x,y) (((x) < (y)) ? (x) : (y)) #define MIN(x,y) (((x) < (y)) ? (x) : (y))
#endif #endif
#ifndef MAX #ifndef MAX
#define MAX(x,y) (((x) > (y)) ? (x) : (y)) #define MAX(x,y) (((x) > (y)) ? (x) : (y))
#endif #endif

19
rdp.c
View File

@ -132,6 +132,25 @@ rdp_out_unistr(STREAM s, char *string, int len)
s->p += len; s->p += len;
} }
/* Input a string in Unicode
*
* Returns str_len of string
*/
int
rdp_in_unistr(STREAM s, char *string, int uni_len)
{
int i = 0;
while (i < uni_len / 2)
{
in_uint8a(s, &string[i++], 1);
in_uint8s(s, 1);
}
return i - 1;
}
/* Parse a logon info packet */ /* Parse a logon info packet */
static void static void
rdp_send_logon_info(uint32 flags, char *domain, char *user, rdp_send_logon_info(uint32 flags, char *domain, char *user,

718
rdpdr.c
View File

@ -6,12 +6,103 @@
#define IRP_MJ_WRITE 0x04 #define IRP_MJ_WRITE 0x04
#define IRP_MJ_DEVICE_CONTROL 0x0e #define IRP_MJ_DEVICE_CONTROL 0x0e
#define IRP_MJ_CREATE 0x00
#define IRP_MJ_CLOSE 0x02
#define IRP_MJ_READ 0x03
#define IRP_MJ_WRITE 0x04
#define IRP_MJ_QUERY_INFORMATION 0x05
#define IRP_MJ_SET_INFORMATION 0x06
#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a
#define IRP_MJ_DIRECTORY_CONTROL 0x0c
#define IRP_MJ_DEVICE_CONTROL 0x0e
#define IRP_MN_QUERY_DIRECTORY 0x01
#define IRP_MN_NOTIFY_CHANGE_DIRECTORY 0x02
#define MAX_ASYNC_IO_REQUESTS 10
extern char hostname[16]; extern char hostname[16];
extern DEVICE_FNS serial_fns; extern DEVICE_FNS serial_fns;
extern DEVICE_FNS printer_fns; extern DEVICE_FNS printer_fns;
extern DEVICE_FNS parallel_fns;
extern DEVICE_FNS disk_fns;
static VCHANNEL *rdpdr_channel; static VCHANNEL *rdpdr_channel;
/* If select() times out, the request for the device with handle g_min_timeout_fd is aborted */
HANDLE g_min_timeout_fd;
uint32 g_num_devices;
/* Table with information about rdpdr devices */
RDPDR_DEVICE g_rdpdr_device[RDPDR_MAX_DEVICES];
/* Used to store incoming io request, until they are ready to be completed */
struct async_iorequest
{
uint32 fd, major, minor, offset, device, id, length;
long timeout, /* Total timeout */
itv_timeout; /* Interval timeout (between serial characters) */
uint8 *buffer;
DEVICE_FNS *fns;
} g_iorequest[MAX_ASYNC_IO_REQUESTS];
/* Return device_id for a given handle */
int
get_device_index(HANDLE handle)
{
int i;
for (i = 0; i < RDPDR_MAX_DEVICES; i++)
{
if (g_rdpdr_device[i].handle == handle)
return i;
}
return -1;
}
/* Converts a windows path to a unix path */
void
convert_to_unix_filename(char *filename)
{
char *p;
while ((p = strchr(filename, '\\')))
{
*p = '/';
}
}
/* Add a new io request to the table containing pending io requests so it won't block rdesktop */
BOOL
add_async_iorequest(uint32 device, uint32 file, uint32 id, uint32 major, uint32 length,
DEVICE_FNS * fns, long total_timeout, long interval_timeout, uint8 * buffer)
{
int i;
struct async_iorequest *iorq;
for (i = 0; i < MAX_ASYNC_IO_REQUESTS; i++)
{
iorq = &g_iorequest[i];
if (iorq->fd == 0)
{
iorq->device = device;
iorq->fd = file;
iorq->id = id;
iorq->major = major;
iorq->length = length;
iorq->fns = fns;
iorq->timeout = total_timeout;
iorq->itv_timeout = interval_timeout;
iorq->buffer = buffer;
return True;
}
}
error("IO request table full. Increase MAX_ASYNC_IO_REQUESTS in rdpdr.c!\n");
return False;
}
void void
rdpdr_send_connect(void) rdpdr_send_connect(void)
{ {
@ -27,6 +118,7 @@ rdpdr_send_connect(void)
channel_send(s, rdpdr_channel); channel_send(s, rdpdr_channel);
} }
void void
rdpdr_send_name(void) rdpdr_send_name(void)
{ {
@ -45,55 +137,79 @@ rdpdr_send_name(void)
channel_send(s, rdpdr_channel); channel_send(s, rdpdr_channel);
} }
/* Returns the size of the payload of the announce packet */
int
announcedata_size()
{
int size, i;
PRINTER *printerinfo;
size = 8; //static announce size
size += g_num_devices * 0x14;
for (i = 0; i < g_num_devices; i++)
{
if (g_rdpdr_device[i].device_type == DEVICE_TYPE_PRINTER)
{
printerinfo = (PRINTER *) g_rdpdr_device[i].pdevice_data;
printerinfo->bloblen =
printercache_load_blob(printerinfo->printer, &(printerinfo->blob));
size += 0x18;
size += 2 * strlen(printerinfo->driver) + 2;
size += 2 * strlen(printerinfo->printer) + 2;
size += printerinfo->bloblen;
}
}
return size;
}
void void
rdpdr_send_available(void) rdpdr_send_available(void)
{ {
uint8 magic[4] = "rDAD"; uint8 magic[4] = "rDAD";
char *driver = "Digital turbo PrintServer 20"; /* Fairly generic PostScript driver */ uint32 driverlen, printerlen, bloblen;
char *printer = "PostScript"; int i;
uint32 driverlen = (strlen(driver) + 1) * 2;
uint32 printerlen = (strlen(printer) + 1) * 2;
STREAM s; STREAM s;
PRINTER *printerinfo;
s = channel_init(rdpdr_channel, 8 + 20); s = channel_init(rdpdr_channel, announcedata_size());
out_uint8a(s, magic, 4); out_uint8a(s, magic, 4);
out_uint32_le(s, 1); /* Number of devices */ out_uint32_le(s, g_num_devices);
#if 1 for (i = 0; i < g_num_devices; i++)
out_uint32_le(s, 0x1); /* Device type 0x1 - serial */ {
out_uint32_le(s, 0); /* Handle */ out_uint32_le(s, g_rdpdr_device[i].device_type);
out_uint8p(s, "COM2", 4); out_uint32_le(s, i); /* RDP Device ID */
out_uint8s(s, 4); /* Pad to 8 */ out_uint8p(s, g_rdpdr_device[i].name, 8);
out_uint32(s, 0);
#endif if (g_rdpdr_device[i].device_type == DEVICE_TYPE_PRINTER)
#if 0 {
out_uint32_le(s, 0x2); /* Device type 0x2 - parallel */ printerinfo = (PRINTER *) g_rdpdr_device[i].pdevice_data;
out_uint32_le(s, 0);
out_uint8p(s, "LPT2", 4); driverlen = 2 * strlen(printerinfo->driver) + 2;
out_uint8s(s, 4); printerlen = 2 * strlen(printerinfo->printer) + 2;
out_uint32(s, 0); bloblen = printerinfo->bloblen;
#endif
#if 1 out_uint32_le(s, 24 + driverlen + printerlen + bloblen); /* length of extra info */
out_uint32_le(s, 0x4); /* Device type 0x4 - printer */ out_uint32_le(s, printerinfo->default_printer ? 2 : 0);
out_uint32_le(s, 1); out_uint8s(s, 8); /* unknown */
out_uint8p(s, "PRN1", 4); out_uint32_le(s, driverlen);
out_uint8s(s, 4); out_uint32_le(s, printerlen);
out_uint32_le(s, 24 + driverlen + printerlen); /* length of extra info */ out_uint32_le(s, bloblen);
out_uint32_le(s, 2); /* unknown */ rdp_out_unistr(s, printerinfo->driver, driverlen - 2);
out_uint8s(s, 8); /* unknown */ rdp_out_unistr(s, printerinfo->printer, printerlen - 2);
out_uint32_le(s, driverlen); /* length of driver name */ out_uint8a(s, printerinfo->blob, bloblen);
out_uint32_le(s, printerlen); /* length of printer name */
out_uint32(s, 0); /* unknown */ xfree(printerinfo->blob); /* Blob is sent twice if reconnecting */
rdp_out_unistr(s, driver, driverlen - 2); }
rdp_out_unistr(s, printer, printerlen - 2); else
#endif {
#if 0 out_uint32(s, 0);
out_uint32_le(s, 0x8); /* Device type 0x8 - disk */ }
out_uint32_le(s, 0); }
out_uint8p(s, "Z:", 2);
out_uint8s(s, 6);
out_uint32(s, 0);
#endif
#if 0 #if 0
out_uint32_le(s, 0x20); /* Device type 0x20 - smart card */ out_uint32_le(s, 0x20); /* Device type 0x20 - smart card */
out_uint32_le(s, 0); out_uint32_le(s, 0);
@ -121,20 +237,37 @@ rdpdr_send_completion(uint32 device, uint32 id, uint32 status, uint32 result, ui
out_uint32_le(s, result); out_uint32_le(s, result);
out_uint8p(s, buffer, length); out_uint8p(s, buffer, length);
s_mark_end(s); s_mark_end(s);
hexdump(s->channel_hdr + 8, s->end - s->channel_hdr - 8); /* JIF
hexdump(s->channel_hdr + 8, s->end - s->channel_hdr - 8); */
channel_send(s, rdpdr_channel); channel_send(s, rdpdr_channel);
} }
static void static void
rdpdr_process_irp(STREAM s) rdpdr_process_irp(STREAM s)
{ {
uint32 device, file, id, major, minor; uint32 result = 0,
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; length = 0,
uint32 result = 0, length, request, bytes_in, bytes_out; desired_access = 0,
uint8 buffer[256]; request,
uint32 buffer_len = 1; file,
info_level,
buffer_len,
id,
major,
minor,
device,
offset,
bytes_in,
bytes_out,
error_mode,
share_mode, disposition, total_timeout, interval_timeout, flags_and_attributes = 0;
char filename[256];
uint8 *buffer, *pst_buf;
struct stream out; struct stream out;
DEVICE_FNS *fns; DEVICE_FNS *fns;
BOOL rw_blocking = True;
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
in_uint32_le(s, device); in_uint32_le(s, device);
in_uint32_le(s, file); in_uint32_le(s, file);
@ -142,16 +275,38 @@ rdpdr_process_irp(STREAM s)
in_uint32_le(s, major); in_uint32_le(s, major);
in_uint32_le(s, minor); in_uint32_le(s, minor);
memset(buffer, 0, sizeof(buffer)); buffer_len = 0;
buffer = (uint8 *) xmalloc(1024);
buffer[0] = 0;
/* FIXME: this should probably be a more dynamic mapping */
switch (device) switch (g_rdpdr_device[device].device_type)
{ {
case 0: case DEVICE_TYPE_SERIAL:
fns = &serial_fns; fns = &serial_fns;
case 1: rw_blocking = False;
break;
case DEVICE_TYPE_PARALLEL:
fns = &parallel_fns;
rw_blocking = False;
break;
case DEVICE_TYPE_PRINTER:
fns = &printer_fns; fns = &printer_fns;
break;
case DEVICE_TYPE_DISK:
fns = &disk_fns;
break;
case DEVICE_TYPE_SCARD:
default: default:
error("IRP for bad device %ld\n", device); error("IRP for bad device %ld\n", device);
return; return;
} }
@ -159,42 +314,232 @@ rdpdr_process_irp(STREAM s)
switch (major) switch (major)
{ {
case IRP_MJ_CREATE: case IRP_MJ_CREATE:
if (fns->create)
status = fns->create(&result); in_uint32_be(s, desired_access);
in_uint8s(s, 0x08); // unknown
in_uint32_le(s, error_mode);
in_uint32_le(s, share_mode);
in_uint32_le(s, disposition);
in_uint32_le(s, flags_and_attributes);
in_uint32_le(s, length);
if (length && (length / 2) < 256)
{
rdp_in_unistr(s, filename, length);
convert_to_unix_filename(filename);
}
else
{
filename[0] = 0;
}
if (!fns->create)
{
status = STATUS_NOT_SUPPORTED;
break;
}
status = fns->create(device, desired_access, share_mode, disposition,
flags_and_attributes, filename, &result);
buffer_len = 1;
break; break;
case IRP_MJ_CLOSE: case IRP_MJ_CLOSE:
if (fns->close) if (!fns->close)
status = fns->close(file); {
status = STATUS_NOT_SUPPORTED;
break;
}
status = fns->close(file);
break; break;
case IRP_MJ_READ: case IRP_MJ_READ:
if (fns->read)
if (!fns->read)
{ {
if (length > sizeof(buffer)) status = STATUS_NOT_SUPPORTED;
length = sizeof(buffer); break;
status = fns->read(file, buffer, length, &result);
buffer_len = result;
} }
in_uint32_le(s, length);
in_uint32_le(s, offset);
#if WITH_DEBUG_RDP5
DEBUG(("RDPDR IRP Read (length: %d, offset: %d)\n", length, offset));
#endif
if (rw_blocking) // Complete read immediately
{
buffer = (uint8 *) xrealloc((void *) buffer, length);
status = fns->read(file, buffer, length, offset, &result);
buffer_len = result;
break;
}
// Add request to table
pst_buf = (uint8 *) xmalloc(length);
serial_get_timeout(file, length, &total_timeout, &interval_timeout);
if (add_async_iorequest
(device, file, id, major, length, fns, total_timeout, interval_timeout,
pst_buf))
{
status = STATUS_PENDING;
break;
}
status = STATUS_CANCELLED;
break; break;
case IRP_MJ_WRITE: case IRP_MJ_WRITE:
if (fns->write)
status = fns->write(file, s->p, length, &result); buffer_len = 1;
if (!fns->write)
{
status = STATUS_NOT_SUPPORTED;
break;
}
in_uint32_le(s, length);
in_uint32_le(s, offset);
in_uint8s(s, 0x18);
#if WITH_DEBUG_RDP5
DEBUG(("RDPDR IRP Write (length: %d)\n", result));
#endif
if (rw_blocking) // Complete immediately
{
status = fns->write(file, s->p, length, offset, &result);
break;
}
// Add to table
pst_buf = (uint8 *) xmalloc(length);
in_uint8a(s, pst_buf, length);
if (add_async_iorequest
(device, file, id, major, length, fns, 0, 0, pst_buf))
{
status = STATUS_PENDING;
break;
}
status = STATUS_CANCELLED;
break;
case IRP_MJ_QUERY_INFORMATION:
if (g_rdpdr_device[device].device_type != DEVICE_TYPE_DISK)
{
status = STATUS_INVALID_HANDLE;
break;
}
in_uint32_le(s, info_level);
out.data = out.p = buffer;
out.size = sizeof(buffer);
status = disk_query_information(file, info_level, &out);
result = buffer_len = out.p - out.data;
break;
case IRP_MJ_SET_INFORMATION:
if (g_rdpdr_device[device].device_type != DEVICE_TYPE_DISK)
{
status = STATUS_INVALID_HANDLE;
break;
}
in_uint32_le(s, info_level);
out.data = out.p = buffer;
out.size = sizeof(buffer);
status = disk_set_information(file, info_level, s, &out);
result = buffer_len = out.p - out.data;
break;
case IRP_MJ_QUERY_VOLUME_INFORMATION:
if (g_rdpdr_device[device].device_type != DEVICE_TYPE_DISK)
{
status = STATUS_INVALID_HANDLE;
break;
}
in_uint32_le(s, info_level);
out.data = out.p = buffer;
out.size = sizeof(buffer);
status = disk_query_volume_information(file, info_level, &out);
result = buffer_len = out.p - out.data;
break;
case IRP_MJ_DIRECTORY_CONTROL:
if (g_rdpdr_device[device].device_type != DEVICE_TYPE_DISK)
{
status = STATUS_INVALID_HANDLE;
break;
}
switch (minor)
{
case IRP_MN_QUERY_DIRECTORY:
in_uint32_le(s, info_level);
in_uint8s(s, 1);
in_uint32_le(s, length);
in_uint8s(s, 0x17);
if (length && length < 2 * 255)
{
rdp_in_unistr(s, filename, length);
convert_to_unix_filename(filename);
}
else
{
filename[0] = 0;
}
out.data = out.p = buffer;
out.size = sizeof(buffer);
status = disk_query_directory(file, info_level, filename,
&out);
result = buffer_len = out.p - out.data;
if (!buffer_len)
buffer_len++;
break;
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
/* JIF
unimpl("IRP major=0x%x minor=0x%x: IRP_MN_NOTIFY_CHANGE_DIRECTORY\n", major, minor); */
status = STATUS_PENDING; // Don't send completion packet
break;
default:
status = STATUS_INVALID_PARAMETER;
/* JIF
unimpl("IRP major=0x%x minor=0x%x\n", major, minor); */
}
break; break;
case IRP_MJ_DEVICE_CONTROL: case IRP_MJ_DEVICE_CONTROL:
if (fns->device_control)
if (!fns->device_control)
{ {
in_uint32_le(s, bytes_out); status = STATUS_NOT_SUPPORTED;
in_uint32_le(s, bytes_in); break;
in_uint32_le(s, request);
in_uint8s(s, 0x14);
out.data = out.p = buffer;
out.size = sizeof(buffer);
status = fns->device_control(file, request, s, &out);
result = buffer_len = out.p - out.data;
} }
in_uint32_le(s, bytes_out);
in_uint32_le(s, bytes_in);
in_uint32_le(s, request);
in_uint8s(s, 0x14);
buffer = (uint8 *) xrealloc((void *) buffer, bytes_out + 0x14);
out.data = out.p = buffer;
out.size = sizeof(buffer);
status = fns->device_control(file, request, s, &out);
result = buffer_len = out.p - out.data;
break; break;
default: default:
@ -202,7 +547,52 @@ rdpdr_process_irp(STREAM s)
break; break;
} }
rdpdr_send_completion(device, id, status, result, buffer, buffer_len); if (status != STATUS_PENDING)
{
rdpdr_send_completion(device, id, status, result, buffer, buffer_len);
}
xfree(buffer);
}
void
rdpdr_send_clientcapabilty(void)
{
uint8 magic[4] = "rDPC";
STREAM s;
s = channel_init(rdpdr_channel, 0x50);
out_uint8a(s, magic, 4);
out_uint32_le(s, 5); /* count */
out_uint16_le(s, 1); /* first */
out_uint16_le(s, 0x28); /* length */
out_uint32_le(s, 1);
out_uint32_le(s, 2);
out_uint16_le(s, 2);
out_uint16_le(s, 5);
out_uint16_le(s, 1);
out_uint16_le(s, 5);
out_uint16_le(s, 0xFFFF);
out_uint16_le(s, 0);
out_uint32_le(s, 0);
out_uint32_le(s, 3);
out_uint32_le(s, 0);
out_uint32_le(s, 0);
out_uint16_le(s, 2); /* second */
out_uint16_le(s, 8); /* length */
out_uint32_le(s, 1);
out_uint16_le(s, 3); /* third */
out_uint16_le(s, 8); /* length */
out_uint32_le(s, 1);
out_uint16_le(s, 4); /* fourth */
out_uint16_le(s, 8); /* length */
out_uint32_le(s, 1);
out_uint16_le(s, 5); /* fifth */
out_uint16_le(s, 8); /* length */
out_uint32_le(s, 1);
s_mark_end(s);
channel_send(s, rdpdr_channel);
} }
static void static void
@ -211,8 +601,10 @@ rdpdr_process(STREAM s)
uint32 handle; uint32 handle;
uint8 *magic; uint8 *magic;
printf("rdpdr_process\n"); #if WITH_DEBUG_RDP5
printf("--- rdpdr_process ---\n");
hexdump(s->p, s->end - s->p); hexdump(s->p, s->end - s->p);
#endif
in_uint8p(s, magic, 4); in_uint8p(s, magic, 4);
if ((magic[0] == 'r') && (magic[1] == 'D')) if ((magic[0] == 'r') && (magic[1] == 'D'))
@ -226,19 +618,35 @@ rdpdr_process(STREAM s)
{ {
rdpdr_send_connect(); rdpdr_send_connect();
rdpdr_send_name(); rdpdr_send_name();
return;
}
if ((magic[2] == 'C') && (magic[3] == 'C'))
{
/* connect from server */
rdpdr_send_clientcapabilty();
rdpdr_send_available(); rdpdr_send_available();
return; return;
} }
else if ((magic[2] == 'C') && (magic[3] == 'C')) if ((magic[2] == 'r') && (magic[3] == 'd'))
{
/* connect from server */
return;
}
else if ((magic[2] == 'r') && (magic[3] == 'd'))
{ {
/* connect to a specific resource */ /* connect to a specific resource */
in_uint32(s, handle); in_uint32(s, handle);
printf("Server connected to resource %d\n", handle); #if WITH_DEBUG_RDP5
DEBUG(("RDPDR: Server connected to resource %d\n", handle));
#endif
return;
}
if ((magic[2] == 'P') && (magic[3] == 'S'))
{
/* server capability */
return;
}
}
if ((magic[0] == 'R') && (magic[1] == 'P'))
{
if ((magic[2] == 'C') && (magic[3] == 'P'))
{
printercache_process(s);
return; return;
} }
} }
@ -246,10 +654,150 @@ rdpdr_process(STREAM s)
} }
BOOL BOOL
rdpdr_init(void) rdpdr_init()
{ {
rdpdr_channel = if (g_num_devices > 0)
channel_register("rdpdr", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_COMPRESS_RDP, {
rdpdr_process); rdpdr_channel = channel_register("rdpdr", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_COMPRESS_RDP, rdpdr_process);
}
return (rdpdr_channel != NULL); return (rdpdr_channel != NULL);
} }
/* Add file descriptors of pending io request to select() */
void
rdpdr_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv, BOOL * timeout)
{
int i;
long select_timeout = 0; // Timeout value to be used for select() (in millisecons).
struct async_iorequest *iorq;
for (i = 0; i < MAX_ASYNC_IO_REQUESTS; i++)
{
iorq = &g_iorequest[i];
if (iorq->fd != 0) // Found a pending io request
{
switch (iorq->major)
{
case IRP_MJ_READ:
FD_SET(iorq->fd, rfds);
// Check if io request timeout is smaller than current (but not 0).
if (iorq->timeout
&& (select_timeout == 0
|| iorq->timeout < select_timeout))
{
// Set new timeout
select_timeout = iorq->timeout;
g_min_timeout_fd = iorq->fd; /* Remember fd */
tv->tv_sec = select_timeout / 1000;
tv->tv_usec = (select_timeout % 1000) * 1000;
*timeout = True;
}
break;
case IRP_MJ_WRITE:
FD_SET(iorq->fd, wfds);
break;
}
*n = MAX(*n, iorq->fd);
}
}
}
/* Check if select() returned with one of the rdpdr file descriptors, and complete io if it did */
void
rdpdr_check_fds(fd_set * rfds, fd_set * wfds, BOOL timed_out)
{
int i;
NTSTATUS status;
uint32 result = 0, buffer_len = 0;
DEVICE_FNS *fns;
struct async_iorequest *iorq;
if (timed_out)
{
rdpdr_abort_io(g_min_timeout_fd, 0, STATUS_TIMEOUT);
return;
}
// Walk through array of pending io_rq's
for (i = 0; i < MAX_ASYNC_IO_REQUESTS; i++)
{
iorq = &g_iorequest[i];
if (iorq->fd != 0)
{
switch (iorq->major)
{
case IRP_MJ_READ:
if (FD_ISSET(iorq->fd, rfds))
{
// Read, and send data.
fns = iorq->fns;
status = fns->read(iorq->fd, iorq->buffer,
iorq->length, 0, &result);
buffer_len = result;
rdpdr_send_completion(iorq->device, iorq->id,
status, result, iorq->buffer,
buffer_len);
#if WITH_DEBUG_RDP5
DEBUG(("RDPDR: %d bytes of data read\n", result));
#endif
xfree(iorq->buffer);
iorq->fd = 0;
}
break;
case IRP_MJ_WRITE:
if (FD_ISSET(iorq->fd, wfds))
{
// Write data and send completion.
fns = iorq->fns;
status = fns->write(iorq->fd, iorq->buffer,
iorq->length, 0, &result);
rdpdr_send_completion(iorq->device, iorq->id,
status, result, "", 1);
xfree(iorq->buffer);
iorq->fd = 0;
}
break;
}
}
}
}
/* Abort a pending io request for a given handle and major */
BOOL
rdpdr_abort_io(uint32 fd, uint32 major, NTSTATUS status)
{
uint32 result;
int i;
struct async_iorequest *iorq;
for (i = 0; i < MAX_ASYNC_IO_REQUESTS; i++)
{
iorq = &g_iorequest[i];
// Only remove from table when major is not set, or when correct major is supplied.
// Abort read should not abort a write io request.
if ((iorq->fd == fd) && (major == 0 || iorq->major == major))
{
result = 0;
rdpdr_send_completion(iorq->device, iorq->id, status, result, "", 1);
xfree(iorq->buffer);
iorq->fd = 0;
return True;
}
}
return False;
}

436
serial.c
View File

@ -52,6 +52,8 @@
#define ODD_PARITY 1 #define ODD_PARITY 1
#define EVEN_PARITY 2 #define EVEN_PARITY 2
extern RDPDR_DEVICE g_rdpdr_device[];
int serial_fd; int serial_fd;
struct termios termios; struct termios termios;
@ -61,121 +63,95 @@ uint32 queue_in_size, queue_out_size;
uint32 wait_mask; uint32 wait_mask;
uint8 stop_bits, parity, word_length; uint8 stop_bits, parity, word_length;
static BOOL SERIAL_DEVICE
get_termios(void) *get_serial_info(HANDLE handle)
{ {
speed_t speed; int index;
if (tcgetattr(serial_fd, &termios) == -1) for (index = 0; index < RDPDR_MAX_DEVICES; index++)
return False; {
if (handle == g_rdpdr_device[index].handle)
return (SERIAL_DEVICE *) g_rdpdr_device[index].pdevice_data;
}
return NULL;
}
speed = cfgetispeed(&termios); BOOL
switch (speed) get_termios(SERIAL_DEVICE *pser_inf, HANDLE serial_fd)
{ {
speed_t speed;
struct termios *ptermios;
ptermios = pser_inf->ptermios;
if (tcgetattr(serial_fd, ptermios) == -1)
return False;
speed = cfgetispeed(ptermios);
switch (speed)
{
#ifdef B75 #ifdef B75
case B75: case B75: pser_inf->baud_rate = 75; break;
baud_rate = 75;
break;
#endif #endif
#ifdef B110 #ifdef B110
case B110: case B110: pser_inf->baud_rate = 110; break;
baud_rate = 110;
break;
#endif #endif
#ifdef B134 #ifdef B134
case B134: case B134: pser_inf->baud_rate = 134; break;
baud_rate = 134;
break;
#endif #endif
#ifdef B150 #ifdef B150
case B150: case B150: pser_inf->baud_rate = 150; break;
baud_rate = 150;
break;
#endif #endif
#ifdef B300 #ifdef B300
case B300: case B300: pser_inf->baud_rate = 300; break;
baud_rate = 300;
break;
#endif #endif
#ifdef B600 #ifdef B600
case B600: case B600: pser_inf->baud_rate = 600; break;
baud_rate = 600;
break;
#endif #endif
#ifdef B1200 #ifdef B1200
case B1200: case B1200: pser_inf->baud_rate = 1200; break;
baud_rate = 1200;
break;
#endif #endif
#ifdef B1800 #ifdef B1800
case B1800: case B1800: pser_inf->baud_rate = 1800; break;
baud_rate = 1800;
break;
#endif #endif
#ifdef B2400 #ifdef B2400
case B2400: case B2400: pser_inf->baud_rate = 2400; break;
baud_rate = 2400;
break;
#endif #endif
#ifdef B4800 #ifdef B4800
case B4800: case B4800: pser_inf->baud_rate = 4800; break;
baud_rate = 4800;
break;
#endif #endif
#ifdef B9600 #ifdef B9600
case B9600: case B9600: pser_inf->baud_rate = 9600; break;
baud_rate = 9600;
break;
#endif #endif
#ifdef B19200 #ifdef B19200
case B19200: case B19200: pser_inf->baud_rate = 19200; break;
baud_rate = 19200;
break;
#endif #endif
#ifdef B38400 #ifdef B38400
case B38400: case B38400: pser_inf->baud_rate = 38400; break;
baud_rate = 38400;
break;
#endif #endif
#ifdef B57600 #ifdef B57600
case B57600: case B57600: pser_inf->baud_rate = 57600; break;
baud_rate = 57600;
break;
#endif #endif
#ifdef B115200 #ifdef B115200
case B115200: case B115200: pser_inf->baud_rate = 115200; break;
baud_rate = 115200;
break;
#endif #endif
default: default: pser_inf->baud_rate = 0; break;
baud_rate = 0; }
break;
}
speed = cfgetospeed(&termios); speed = cfgetospeed(ptermios);
dtr = (speed == B0) ? 0 : 1; pser_inf->dtr = (speed == B0) ? 0 : 1;
stop_bits = (termios.c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BITS_1; pser_inf->stop_bits = (ptermios->c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BITS_1;
parity = (termios. pser_inf->parity = (ptermios->c_cflag & PARENB) ? ((ptermios->c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY) : NO_PARITY;
c_cflag & PARENB) ? ((termios. switch (ptermios->c_cflag & CSIZE)
c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY) : NO_PARITY; {
switch (termios.c_cflag & CSIZE) case CS5: pser_inf->word_length = 5; break;
{ case CS6: pser_inf->word_length = 6; break;
case CS5: case CS7: pser_inf->word_length = 7; break;
word_length = 5; default: pser_inf->word_length = 8; break;
break; }
case CS6:
word_length = 6;
break;
case CS7:
word_length = 7;
break;
default:
word_length = 8;
break;
}
return True; return True;
} }
static void static void
@ -186,83 +162,51 @@ set_termios(void)
switch (baud_rate) switch (baud_rate)
{ {
#ifdef B75 #ifdef B75
case 75: case 75: speed = B75;break;
speed = B75;
break;
#endif #endif
#ifdef B110 #ifdef B110
case 110: case 110: speed = B110;break;
speed = B110;
break;
#endif #endif
#ifdef B134 #ifdef B134
case 134: case 134: speed = B134;break;
speed = B134;
break;
#endif #endif
#ifdef B150 #ifdef B150
case 150: case 150: speed = B150;break;
speed = B150;
break;
#endif #endif
#ifdef B300 #ifdef B300
case 300: case 300: speed = B300;break;
speed = B300;
break;
#endif #endif
#ifdef B600 #ifdef B600
case 600: case 600: speed = B600;break;
speed = B600;
break;
#endif #endif
#ifdef B1200 #ifdef B1200
case 1200: case 1200: speed = B1200;break;
speed = B1200;
break;
#endif #endif
#ifdef B1800 #ifdef B1800
case 1800: case 1800: speed = B1800;break;
speed = B1800;
break;
#endif #endif
#ifdef B2400 #ifdef B2400
case 2400: case 2400: speed = B2400;break;
speed = B2400;
break;
#endif #endif
#ifdef B4800 #ifdef B4800
case 4800: case 4800: speed = B4800;break;
speed = B4800;
break;
#endif #endif
#ifdef B9600 #ifdef B9600
case 9600: case 9600: speed = B9600;break;
speed = B9600;
break;
#endif #endif
#ifdef B19200 #ifdef B19200
case 19200: case 19200: speed = B19200;break;
speed = B19200;
break;
#endif #endif
#ifdef B38400 #ifdef B38400
case 38400: case 38400: speed = B38400;break;
speed = B38400;
break;
#endif #endif
#ifdef B57600 #ifdef B57600
case 57600: case 57600: speed = B57600;break;
speed = B57600;
break;
#endif #endif
#ifdef B115200 #ifdef B115200
case 115200: case 115200: speed = B115200;break;
speed = B115200;
break;
#endif #endif
default: default: speed = B0;break;
speed = B0;
break;
} }
/* on systems with separate ispeed and ospeed, we can remember the speed /* on systems with separate ispeed and ospeed, we can remember the speed
@ -305,19 +249,151 @@ set_termios(void)
tcsetattr(serial_fd, TCSANOW, &termios); tcsetattr(serial_fd, TCSANOW, &termios);
} }
static NTSTATUS /* Enumeration of devices from rdesktop.c */
serial_create(HANDLE * handle) /* returns numer of units found and initialized. */
/* optarg looks like ':com1=/dev/ttyS0' */
/* when it arrives to this function. */
/* windev u*dev baud, parity, stop bits, wordlength */
/* :com1=/dev/ttyS0:9600,0|1|2,0|2,5|6|7|8:dtr */
int
serial_enum_devices(int *id, char* optarg)
{ {
/* XXX do we have to handle concurrent open attempts? */ SERIAL_DEVICE* pser_inf;
serial_fd = open("/dev/ttyS0", O_RDWR);
if (serial_fd == -1)
return STATUS_ACCESS_DENIED;
if (!get_termios()) int argcount=0;
return STATUS_ACCESS_DENIED; char* pos = optarg;
char* pos2;
char* pos3;
*handle = 0; if(*id<RDPDR_MAX_DEVICES){
return STATUS_SUCCESS; // Init data structures for device
pser_inf = (SERIAL_DEVICE *) xmalloc(sizeof(SERIAL_DEVICE));
pser_inf->ptermios = (struct termios *) xmalloc(sizeof(struct termios));
pser_inf->pold_termios = (struct termios *) xmalloc(sizeof(struct termios));
// skip the first colon
optarg++;
while( (pos = next_arg( optarg, ':')) ){
switch(argcount){
/* com1=/dev/ttyS0 */
case 0:
pos2 = next_arg(optarg,'=');
if( !pos2 || *pos2 == (char)0x00 ){
error("-r comport arguments should look like: -r comport:com1=/dev/ttyS0\n");
return 0;
}
/* optarg = com1, pos2 = /dev/ttyS0 */
strcpy(g_rdpdr_device[*id].name,optarg);
toupper(g_rdpdr_device[*id].name);
g_rdpdr_device[*id].local_path = xmalloc( strlen(pos2) + 1 );
strcpy(g_rdpdr_device[*id].local_path,pos2);
break;
/* 9600,0|1|2,O|2,5|6|7|8 */
/* TODO: values should be set in serial_create()... ??? */
case 1:
pos2 = next_arg(optarg,',');
/*optarg=9600*/
pser_inf->baud_rate = atoi(optarg);
if( !pos2 || *pos2 == (char)0x00 )
break;
pos3 = next_arg(pos2,',');
/* pos2 = 0|1|2 */
pser_inf->parity = atoi(pos2);
/* pos3 = 0|2,5|6|7|8*/
pos2 = next_arg(pos3,',');
if( !pos3 || *pos3 == (char)0x00 )
break;
pser_inf->stop_bits = atoi(pos3);
/* pos2 = 5|6|7|8 */
if( !pos2 || *pos2 == (char)0x00 )
break;
pser_inf->word_length = atoi(pos2);
break;
default:
if( (*optarg != (char)0x00) && (strcmp( optarg, "dtr" ) == 0) ){
pser_inf->dtr = 1;
}
/* TODO: add more switches here, like xon, xoff. they will be separated by colon
if( (*optarg != (char)0x00) && (strcmp( optarg, "xon" ) == 0) ){
}
*/
break;
}
argcount++;
optarg=pos;
}
printf("SERIAL %s to %s", g_rdpdr_device[*id].name, g_rdpdr_device[*id].local_path );
if( pser_inf->baud_rate != 0 ){
printf(" with baud: %u, parity: %u, stop bits: %u word length: %u", pser_inf->baud_rate, pser_inf->parity, pser_inf->stop_bits, pser_inf->word_length );
if( pser_inf->dtr )
printf( " dtr set\n");
else
printf( "\n" );
}else
printf("\n");
// set device type
g_rdpdr_device[*id].device_type = DEVICE_TYPE_SERIAL;
g_rdpdr_device[*id].pdevice_data = (void *) pser_inf;
(*id)++;
return 1;
}
return 0;
}
NTSTATUS
serial_create(uint32 device_id, uint32 access, uint32 share_mode, uint32 disposition, uint32 flags_and_attributes, char *filename, HANDLE *handle)
{
HANDLE serial_fd;
SERIAL_DEVICE *pser_inf;
struct termios *ptermios;
SERIAL_DEVICE tmp_inf;
pser_inf = (SERIAL_DEVICE *) g_rdpdr_device[device_id].pdevice_data;
ptermios = pser_inf->ptermios;
serial_fd = open(g_rdpdr_device[device_id].local_path, O_RDWR | O_NOCTTY);
if (serial_fd == -1)
return STATUS_ACCESS_DENIED;
// before we clog the user inserted args store them locally
//
memcpy(&tmp_inf,pser_inf, sizeof(pser_inf) );
if (!get_termios(pser_inf, serial_fd))
return STATUS_ACCESS_DENIED;
// Store handle for later use
g_rdpdr_device[device_id].handle = serial_fd;
tcgetattr(serial_fd, pser_inf->pold_termios); // Backup original settings
// Initial configuration.
bzero(ptermios, sizeof(ptermios));
ptermios->c_cflag = B9600 | CRTSCTS | CS8 | CLOCAL | CREAD;
ptermios->c_iflag = IGNPAR;
ptermios->c_oflag = 0;
ptermios->c_lflag = 0; //non-canonical, no echo
ptermios->c_cc[VTIME] = 0;
tcsetattr(serial_fd, TCSANOW, ptermios);
// overload with user settings
// -- if there are any
if( tmp_inf.baud_rate != 0 ){
dtr = tmp_inf.dtr;
baud_rate = tmp_inf.baud_rate;
parity = tmp_inf.parity;
stop_bits = tmp_inf.stop_bits;
word_length = tmp_inf.word_length;
set_termios();
}
*handle = serial_fd;
return STATUS_SUCCESS;
} }
static NTSTATUS static NTSTATUS
@ -327,18 +403,52 @@ serial_close(HANDLE handle)
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
static NTSTATUS NTSTATUS
serial_read(HANDLE handle, uint8 * data, uint32 length, uint32 * result) serial_read(HANDLE handle, uint8 *data, uint32 length, uint32 offset, uint32 *result)
{ {
*result = read(serial_fd, data, length); long timeout;
return STATUS_SUCCESS; SERIAL_DEVICE *pser_inf;
struct termios *ptermios;
timeout = 0;
pser_inf = get_serial_info(handle);
ptermios = pser_inf->ptermios;
// Set timeouts kind of like the windows serial timeout parameters. Multiply timeout
// with requested read size
if (pser_inf->read_total_timeout_multiplier | pser_inf->read_total_timeout_constant)
{
timeout = (pser_inf->read_total_timeout_multiplier * length + pser_inf->read_total_timeout_constant + 99) / 100;
}
else if (pser_inf->read_interval_timeout)
{
timeout = (pser_inf->read_interval_timeout * length + 99) / 100;
}
// If a timeout is set, do a blocking read, which times out after some time.
// It will make rdesktop less responsive, but it will improve serial performance, by not
// reading one character at a time.
if (timeout == 0)
{
ptermios->c_cc[VTIME] = 0;
ptermios->c_cc[VMIN] = 0;
}
else
{
ptermios->c_cc[VTIME] = timeout;
ptermios->c_cc[VMIN] = 1;
}
tcsetattr(handle, TCSANOW, ptermios);
*result = read(handle, data, length);
return STATUS_SUCCESS;
} }
static NTSTATUS NTSTATUS
serial_write(HANDLE handle, uint8 * data, uint32 length, uint32 * result) serial_write(HANDLE handle, uint8 *data, uint32 length, uint32 offset, uint32 *result)
{ {
*result = write(serial_fd, data, length); *result = write(handle, data, length);
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
static NTSTATUS static NTSTATUS
@ -382,7 +492,7 @@ serial_device_control(HANDLE handle, uint32 request, STREAM in, STREAM out)
break; break;
case SERIAL_IMMEDIATE_CHAR: case SERIAL_IMMEDIATE_CHAR:
in_uint8(in, immediate); in_uint8(in, immediate);
serial_write(handle, &immediate, 1, &result); serial_write(handle, &immediate, 1, 0, &result);
break; break;
case SERIAL_CONFIG_SIZE: case SERIAL_CONFIG_SIZE:
out_uint32_le(out, 0); out_uint32_le(out, 0);
@ -430,9 +540,16 @@ serial_device_control(HANDLE handle, uint32 request, STREAM in, STREAM out)
tcsendbreak(serial_fd, 0); tcsendbreak(serial_fd, 0);
break; break;
case SERIAL_PURGE: case SERIAL_PURGE:
in_uint32(purge_mask);
/* tcflush */ printf("SERIAL_PURGE\n");
break; in_uint32(in, purge_mask);
if (purge_mask & 0x04) flush_mask |= TCOFLUSH;
if (purge_mask & 0x08) flush_mask |= TCIFLUSH;
if (flush_mask != 0) tcflush(handle, flush_mask);
if (purge_mask & 0x01) rdpdr_abort_io(handle, 4, STATUS_CANCELLED);
if (purge_mask & 0x02) rdpdr_abort_io(handle, 3, STATUS_CANCELLED);
break;
case SERIAL_RESET_DEVICE: case SERIAL_RESET_DEVICE:
case SERIAL_SET_BREAK_OFF: case SERIAL_SET_BREAK_OFF:
case SERIAL_SET_RTS: case SERIAL_SET_RTS:
@ -451,6 +568,27 @@ serial_device_control(HANDLE handle, uint32 request, STREAM in, STREAM out)
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
/* Read timeout for a given file descripter (device) when adding fd's to select() */
BOOL
serial_get_timeout(uint32 handle, uint32 length, uint32 *timeout, uint32 *itv_timeout)
{
int index;
SERIAL_DEVICE *pser_inf;
index = get_device_index(handle);
if (g_rdpdr_device[index].device_type != DEVICE_TYPE_SERIAL)
{
return False;
}
pser_inf = (SERIAL_DEVICE *) g_rdpdr_device[index].pdevice_data;
*timeout = pser_inf->read_total_timeout_multiplier * length + pser_inf->read_total_timeout_constant;
*itv_timeout = pser_inf->read_interval_timeout;
return True;
}
DEVICE_FNS serial_fns = { DEVICE_FNS serial_fns = {
serial_create, serial_create,
serial_close, serial_close,

75
types.h
View File

@ -7,7 +7,7 @@
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
@ -140,10 +140,73 @@ typedef uint32 HANDLE;
typedef struct _DEVICE_FNS typedef struct _DEVICE_FNS
{ {
NTSTATUS(*create) (HANDLE * handle); NTSTATUS (*create)(uint32 device, uint32 desired_access, uint32 share_mode, uint32 create_disposition, uint32 flags_and_attributes, char *filename, HANDLE *handle);
NTSTATUS(*close) (HANDLE handle); NTSTATUS (*close)(HANDLE handle);
NTSTATUS(*read) (HANDLE handle, uint8 * data, uint32 length, uint32 * result); NTSTATUS (*read)(HANDLE handle, uint8 *data, uint32 length, uint32 offset, uint32 *result);
NTSTATUS(*write) (HANDLE handle, uint8 * data, uint32 length, uint32 * result); NTSTATUS (*write)(HANDLE handle, uint8 *data, uint32 length, uint32 offset, uint32 *result);
NTSTATUS(*device_control) (HANDLE handle, uint32 request, STREAM in, STREAM out); NTSTATUS (*device_control)(HANDLE handle, uint32 request, STREAM in, STREAM out);
} }
DEVICE_FNS; DEVICE_FNS;
typedef struct rdpdr_device_info
{
uint32 device_type;
HANDLE handle;
char name[8];
char *local_path;
void *pdevice_data;
}
RDPDR_DEVICE;
typedef struct rdpdr_serial_device_info
{
int dtr;
uint32 baud_rate,
queue_in_size,
queue_out_size,
wait_mask,
read_interval_timeout,
read_total_timeout_multiplier,
read_total_timeout_constant,
write_total_timeout_multiplier,
write_total_timeout_constant,
posix_wait_mask;
uint8 stop_bits,
parity,
word_length;
struct termios *ptermios,
*pold_termios;
}
SERIAL_DEVICE;
typedef struct rdpdr_parallel_device_info
{
char *driver,
*printer;
uint32 queue_in_size,
queue_out_size,
wait_mask,
read_interval_timeout,
read_total_timeout_multiplier,
read_total_timeout_constant,
write_total_timeout_multiplier,
write_total_timeout_constant,
posix_wait_mask,
bloblen;
uint8 *blob;
}
PARALLEL_DEVICE;
typedef struct rdpdr_printer_info
{
FILE *printer_fp;
char *driver,
*printer;
uint32 bloblen;
uint8 *blob;
BOOL default_printer;
}
PRINTER;