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
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
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

View File

@ -317,3 +317,20 @@ enum RDP_INPUT_DEVICE
#define STATUS_INVALID_PARAMETER 0xc000000d
#define STATUS_INVALID_DEVICE_REQUEST 0xc0000010
#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"
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
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");
*handle = 0;
char cmd[256];
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;
}
static NTSTATUS
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;
}
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;
}

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(uint8 * data, uint32 length);
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 */
int get_current_workarea(uint32 * x, uint32 * y, uint32 * width, uint32 * height);
/* iso.c */
@ -46,7 +52,13 @@ void mcs_disconnect(void);
/* orders.c */
void process_orders(STREAM s, uint16 num_orders);
void reset_order_state(void);
/* parallel.c */
int parallel_enum_devices(int *id, char *optarg);
/* 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 */
int main(int argc, char *argv[]);
void generate_random(uint8 * random);
@ -56,13 +68,17 @@ void xfree(void *mem);
void error(char *format, ...);
void warning(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);
void save_licence(unsigned char *data, int length);
char *next_arg(char *src, char needle);
/* rdp5.c */
void rdp5_process(STREAM s, BOOL encryption);
/* rdp.c */
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,
uint16 param2);
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);
void rdp_disconnect(void);
/* rdpdr.c */
void convert_to_unix_filename(char *filename);
void rdpdr_send_connect(void);
void rdpdr_send_name(void);
void rdpdr_send_available(void);
void rdpdr_send_completion(uint32 device, uint32 id, uint32 status, uint32 result, uint8 * buffer,
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 */
STREAM rdpsnd_init_packet(uint16 type, uint16 size);
void rdpsnd_send(STREAM s);
@ -112,6 +131,8 @@ STREAM sec_recv(void);
BOOL sec_connect(char *server, char *username);
void sec_disconnect(void);
/* 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 */
STREAM tcp_init(uint32 maxlen);
void tcp_send(STREAM s);

View File

@ -1,7 +1,7 @@
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
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
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;
#endif
extern RDPDR_DEVICE g_rdpdr_device[];
extern uint32 g_num_devices;
#ifdef RDP2VNC
extern int rfb_port;
extern int defer_time;
@ -111,9 +114,18 @@ usage(char *program)
fprintf(stderr, " -K: keep window manager key bindings\n");
fprintf(stderr, " -S: caption button size (single application mode)\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, " -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, " -4: use RDP version 4\n");
fprintf(stderr, " -5: use RDP version 5 (default)\n");
@ -218,6 +230,7 @@ main(int argc, char *argv[])
uint32 flags;
char *p;
int c;
int username_option = 0;
flags = RDP_LOGON_NORMAL;
@ -225,6 +238,8 @@ main(int argc, char *argv[])
domain[0] = password[0] = shell[0] = directory[0] = 0;
strcpy(keymapname, "en-us");
g_num_devices = 0;
#ifdef RDP2VNC
#define VNCOPT "V:Q:"
#else
@ -385,12 +400,36 @@ main(int argc, char *argv[])
break;
case 'r':
if (!strcmp(optarg, "sound"))
if (strncmp("sound", optarg, 5) == 0)
{
#ifdef WITH_RDPSND
g_rdpsnd = True;
#else
warning("Not compiled with sound support");
#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;
case '0':
@ -470,7 +509,7 @@ main(int argc, char *argv[])
if (g_rdpsnd)
rdpsnd_init();
#endif
/* rdpdr_init(); */
rdpdr_init();
if (!rdp_connect(server, flags, domain, password, shell, directory))
return 1;
@ -660,7 +699,7 @@ unimpl(char *format, ...)
/* produce a hex dump */
void
hexdump(unsigned char *p, int len)
hexdump(unsigned char *p, unsigned int len)
{
unsigned char *line = p;
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
load_licence(unsigned char **data)

View File

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

19
rdp.c
View File

@ -132,6 +132,25 @@ rdp_out_unistr(STREAM s, char *string, int 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 */
static void
rdp_send_logon_info(uint32 flags, char *domain, char *user,

696
rdpdr.c
View File

@ -6,12 +6,103 @@
#define IRP_MJ_WRITE 0x04
#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 DEVICE_FNS serial_fns;
extern DEVICE_FNS printer_fns;
extern DEVICE_FNS parallel_fns;
extern DEVICE_FNS disk_fns;
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
rdpdr_send_connect(void)
{
@ -27,6 +118,7 @@ rdpdr_send_connect(void)
channel_send(s, rdpdr_channel);
}
void
rdpdr_send_name(void)
{
@ -45,55 +137,79 @@ rdpdr_send_name(void)
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
rdpdr_send_available(void)
{
uint8 magic[4] = "rDAD";
char *driver = "Digital turbo PrintServer 20"; /* Fairly generic PostScript driver */
char *printer = "PostScript";
uint32 driverlen = (strlen(driver) + 1) * 2;
uint32 printerlen = (strlen(printer) + 1) * 2;
uint32 driverlen, printerlen, bloblen;
int i;
STREAM s;
PRINTER *printerinfo;
s = channel_init(rdpdr_channel, 8 + 20);
s = channel_init(rdpdr_channel, announcedata_size());
out_uint8a(s, magic, 4);
out_uint32_le(s, 1); /* Number of devices */
out_uint32_le(s, g_num_devices);
#if 1
out_uint32_le(s, 0x1); /* Device type 0x1 - serial */
out_uint32_le(s, 0); /* Handle */
out_uint8p(s, "COM2", 4);
out_uint8s(s, 4); /* Pad to 8 */
out_uint32(s, 0);
#endif
#if 0
out_uint32_le(s, 0x2); /* Device type 0x2 - parallel */
out_uint32_le(s, 0);
out_uint8p(s, "LPT2", 4);
out_uint8s(s, 4);
out_uint32(s, 0);
#endif
#if 1
out_uint32_le(s, 0x4); /* Device type 0x4 - printer */
out_uint32_le(s, 1);
out_uint8p(s, "PRN1", 4);
out_uint8s(s, 4);
out_uint32_le(s, 24 + driverlen + printerlen); /* length of extra info */
out_uint32_le(s, 2); /* unknown */
for (i = 0; i < g_num_devices; i++)
{
out_uint32_le(s, g_rdpdr_device[i].device_type);
out_uint32_le(s, i); /* RDP Device ID */
out_uint8p(s, g_rdpdr_device[i].name, 8);
if (g_rdpdr_device[i].device_type == DEVICE_TYPE_PRINTER)
{
printerinfo = (PRINTER *) g_rdpdr_device[i].pdevice_data;
driverlen = 2 * strlen(printerinfo->driver) + 2;
printerlen = 2 * strlen(printerinfo->printer) + 2;
bloblen = printerinfo->bloblen;
out_uint32_le(s, 24 + driverlen + printerlen + bloblen); /* length of extra info */
out_uint32_le(s, printerinfo->default_printer ? 2 : 0);
out_uint8s(s, 8); /* unknown */
out_uint32_le(s, driverlen); /* length of driver name */
out_uint32_le(s, printerlen); /* length of printer name */
out_uint32(s, 0); /* unknown */
rdp_out_unistr(s, driver, driverlen - 2);
rdp_out_unistr(s, printer, printerlen - 2);
#endif
#if 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_le(s, driverlen);
out_uint32_le(s, printerlen);
out_uint32_le(s, bloblen);
rdp_out_unistr(s, printerinfo->driver, driverlen - 2);
rdp_out_unistr(s, printerinfo->printer, printerlen - 2);
out_uint8a(s, printerinfo->blob, bloblen);
xfree(printerinfo->blob); /* Blob is sent twice if reconnecting */
}
else
{
out_uint32(s, 0);
#endif
}
}
#if 0
out_uint32_le(s, 0x20); /* Device type 0x20 - smart card */
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_uint8p(s, buffer, length);
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);
}
static void
rdpdr_process_irp(STREAM s)
{
uint32 device, file, id, major, minor;
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
uint32 result = 0, length, request, bytes_in, bytes_out;
uint8 buffer[256];
uint32 buffer_len = 1;
uint32 result = 0,
length = 0,
desired_access = 0,
request,
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;
DEVICE_FNS *fns;
BOOL rw_blocking = True;
NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;
in_uint32_le(s, device);
in_uint32_le(s, file);
@ -142,16 +275,38 @@ rdpdr_process_irp(STREAM s)
in_uint32_le(s, major);
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;
case 1:
rw_blocking = False;
break;
case DEVICE_TYPE_PARALLEL:
fns = &parallel_fns;
rw_blocking = False;
break;
case DEVICE_TYPE_PRINTER:
fns = &printer_fns;
break;
case DEVICE_TYPE_DISK:
fns = &disk_fns;
break;
case DEVICE_TYPE_SCARD:
default:
error("IRP for bad device %ld\n", device);
return;
}
@ -159,42 +314,232 @@ rdpdr_process_irp(STREAM s)
switch (major)
{
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;
case IRP_MJ_CLOSE:
if (fns->close)
if (!fns->close)
{
status = STATUS_NOT_SUPPORTED;
break;
}
status = fns->close(file);
break;
case IRP_MJ_READ:
if (fns->read)
if (!fns->read)
{
if (length > sizeof(buffer))
length = sizeof(buffer);
status = fns->read(file, buffer, length, &result);
buffer_len = result;
status = STATUS_NOT_SUPPORTED;
break;
}
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;
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;
case IRP_MJ_DEVICE_CONTROL:
if (fns->device_control)
if (!fns->device_control)
{
status = STATUS_NOT_SUPPORTED;
break;
}
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;
default:
@ -202,8 +547,53 @@ rdpdr_process_irp(STREAM s)
break;
}
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
rdpdr_process(STREAM s)
@ -211,8 +601,10 @@ rdpdr_process(STREAM s)
uint32 handle;
uint8 *magic;
printf("rdpdr_process\n");
#if WITH_DEBUG_RDP5
printf("--- rdpdr_process ---\n");
hexdump(s->p, s->end - s->p);
#endif
in_uint8p(s, magic, 4);
if ((magic[0] == 'r') && (magic[1] == 'D'))
@ -226,19 +618,35 @@ rdpdr_process(STREAM s)
{
rdpdr_send_connect();
rdpdr_send_name();
return;
}
if ((magic[2] == 'C') && (magic[3] == 'C'))
{
/* connect from server */
rdpdr_send_clientcapabilty();
rdpdr_send_available();
return;
}
else if ((magic[2] == 'C') && (magic[3] == 'C'))
{
/* connect from server */
return;
}
else if ((magic[2] == 'r') && (magic[3] == 'd'))
if ((magic[2] == 'r') && (magic[3] == 'd'))
{
/* connect to a specific resource */
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;
}
}
@ -246,10 +654,150 @@ rdpdr_process(STREAM s)
}
BOOL
rdpdr_init(void)
rdpdr_init()
{
rdpdr_channel =
channel_register("rdpdr", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_COMPRESS_RDP,
rdpdr_process);
if (g_num_devices > 0)
{
rdpdr_channel = channel_register("rdpdr", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_COMPRESS_RDP, rdpdr_process);
}
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;
}

406
serial.c
View File

@ -52,6 +52,8 @@
#define ODD_PARITY 1
#define EVEN_PARITY 2
extern RDPDR_DEVICE g_rdpdr_device[];
int serial_fd;
struct termios termios;
@ -61,118 +63,92 @@ uint32 queue_in_size, queue_out_size;
uint32 wait_mask;
uint8 stop_bits, parity, word_length;
static BOOL
get_termios(void)
SERIAL_DEVICE
*get_serial_info(HANDLE handle)
{
int index;
for (index = 0; index < RDPDR_MAX_DEVICES; index++)
{
if (handle == g_rdpdr_device[index].handle)
return (SERIAL_DEVICE *) g_rdpdr_device[index].pdevice_data;
}
return NULL;
}
BOOL
get_termios(SERIAL_DEVICE *pser_inf, HANDLE serial_fd)
{
speed_t speed;
struct termios *ptermios;
if (tcgetattr(serial_fd, &termios) == -1)
ptermios = pser_inf->ptermios;
if (tcgetattr(serial_fd, ptermios) == -1)
return False;
speed = cfgetispeed(&termios);
speed = cfgetispeed(ptermios);
switch (speed)
{
#ifdef B75
case B75:
baud_rate = 75;
break;
case B75: pser_inf->baud_rate = 75; break;
#endif
#ifdef B110
case B110:
baud_rate = 110;
break;
case B110: pser_inf->baud_rate = 110; break;
#endif
#ifdef B134
case B134:
baud_rate = 134;
break;
case B134: pser_inf->baud_rate = 134; break;
#endif
#ifdef B150
case B150:
baud_rate = 150;
break;
case B150: pser_inf->baud_rate = 150; break;
#endif
#ifdef B300
case B300:
baud_rate = 300;
break;
case B300: pser_inf->baud_rate = 300; break;
#endif
#ifdef B600
case B600:
baud_rate = 600;
break;
case B600: pser_inf->baud_rate = 600; break;
#endif
#ifdef B1200
case B1200:
baud_rate = 1200;
break;
case B1200: pser_inf->baud_rate = 1200; break;
#endif
#ifdef B1800
case B1800:
baud_rate = 1800;
break;
case B1800: pser_inf->baud_rate = 1800; break;
#endif
#ifdef B2400
case B2400:
baud_rate = 2400;
break;
case B2400: pser_inf->baud_rate = 2400; break;
#endif
#ifdef B4800
case B4800:
baud_rate = 4800;
break;
case B4800: pser_inf->baud_rate = 4800; break;
#endif
#ifdef B9600
case B9600:
baud_rate = 9600;
break;
case B9600: pser_inf->baud_rate = 9600; break;
#endif
#ifdef B19200
case B19200:
baud_rate = 19200;
break;
case B19200: pser_inf->baud_rate = 19200; break;
#endif
#ifdef B38400
case B38400:
baud_rate = 38400;
break;
case B38400: pser_inf->baud_rate = 38400; break;
#endif
#ifdef B57600
case B57600:
baud_rate = 57600;
break;
case B57600: pser_inf->baud_rate = 57600; break;
#endif
#ifdef B115200
case B115200:
baud_rate = 115200;
break;
case B115200: pser_inf->baud_rate = 115200; break;
#endif
default:
baud_rate = 0;
break;
default: pser_inf->baud_rate = 0; break;
}
speed = cfgetospeed(&termios);
dtr = (speed == B0) ? 0 : 1;
speed = cfgetospeed(ptermios);
pser_inf->dtr = (speed == B0) ? 0 : 1;
stop_bits = (termios.c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BITS_1;
parity = (termios.
c_cflag & PARENB) ? ((termios.
c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY) : NO_PARITY;
switch (termios.c_cflag & CSIZE)
pser_inf->stop_bits = (ptermios->c_cflag & CSTOPB) ? STOP_BITS_2 : STOP_BITS_1;
pser_inf->parity = (ptermios->c_cflag & PARENB) ? ((ptermios->c_cflag & PARODD) ? ODD_PARITY : EVEN_PARITY) : NO_PARITY;
switch (ptermios->c_cflag & CSIZE)
{
case CS5:
word_length = 5;
break;
case CS6:
word_length = 6;
break;
case CS7:
word_length = 7;
break;
default:
word_length = 8;
break;
case CS5: pser_inf->word_length = 5; break;
case CS6: pser_inf->word_length = 6; break;
case CS7: pser_inf->word_length = 7; break;
default: pser_inf->word_length = 8; break;
}
return True;
@ -186,83 +162,51 @@ set_termios(void)
switch (baud_rate)
{
#ifdef B75
case 75:
speed = B75;
break;
case 75: speed = B75;break;
#endif
#ifdef B110
case 110:
speed = B110;
break;
case 110: speed = B110;break;
#endif
#ifdef B134
case 134:
speed = B134;
break;
case 134: speed = B134;break;
#endif
#ifdef B150
case 150:
speed = B150;
break;
case 150: speed = B150;break;
#endif
#ifdef B300
case 300:
speed = B300;
break;
case 300: speed = B300;break;
#endif
#ifdef B600
case 600:
speed = B600;
break;
case 600: speed = B600;break;
#endif
#ifdef B1200
case 1200:
speed = B1200;
break;
case 1200: speed = B1200;break;
#endif
#ifdef B1800
case 1800:
speed = B1800;
break;
case 1800: speed = B1800;break;
#endif
#ifdef B2400
case 2400:
speed = B2400;
break;
case 2400: speed = B2400;break;
#endif
#ifdef B4800
case 4800:
speed = B4800;
break;
case 4800: speed = B4800;break;
#endif
#ifdef B9600
case 9600:
speed = B9600;
break;
case 9600: speed = B9600;break;
#endif
#ifdef B19200
case 19200:
speed = B19200;
break;
case 19200: speed = B19200;break;
#endif
#ifdef B38400
case 38400:
speed = B38400;
break;
case 38400: speed = B38400;break;
#endif
#ifdef B57600
case 57600:
speed = B57600;
break;
case 57600: speed = B57600;break;
#endif
#ifdef B115200
case 115200:
speed = B115200;
break;
case 115200: speed = B115200;break;
#endif
default:
speed = B0;
break;
default: speed = B0;break;
}
/* on systems with separate ispeed and ospeed, we can remember the speed
@ -305,18 +249,150 @@ set_termios(void)
tcsetattr(serial_fd, TCSANOW, &termios);
}
static NTSTATUS
serial_create(HANDLE * handle)
/* Enumeration of devices from rdesktop.c */
/* 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_fd = open("/dev/ttyS0", O_RDWR);
SERIAL_DEVICE* pser_inf;
int argcount=0;
char* pos = optarg;
char* pos2;
char* pos3;
if(*id<RDPDR_MAX_DEVICES){
// 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;
if (!get_termios())
// 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;
*handle = 0;
// 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;
}
@ -327,17 +403,51 @@ serial_close(HANDLE handle)
return STATUS_SUCCESS;
}
static NTSTATUS
serial_read(HANDLE handle, uint8 * data, uint32 length, uint32 * result)
NTSTATUS
serial_read(HANDLE handle, uint8 *data, uint32 length, uint32 offset, uint32 *result)
{
*result = read(serial_fd, data, length);
long timeout;
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
serial_write(HANDLE handle, uint8 * data, uint32 length, uint32 * result)
NTSTATUS
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;
}
@ -382,7 +492,7 @@ serial_device_control(HANDLE handle, uint32 request, STREAM in, STREAM out)
break;
case SERIAL_IMMEDIATE_CHAR:
in_uint8(in, immediate);
serial_write(handle, &immediate, 1, &result);
serial_write(handle, &immediate, 1, 0, &result);
break;
case SERIAL_CONFIG_SIZE:
out_uint32_le(out, 0);
@ -430,9 +540,16 @@ serial_device_control(HANDLE handle, uint32 request, STREAM in, STREAM out)
tcsendbreak(serial_fd, 0);
break;
case SERIAL_PURGE:
in_uint32(purge_mask);
/* tcflush */
printf("SERIAL_PURGE\n");
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_SET_BREAK_OFF:
case SERIAL_SET_RTS:
@ -451,6 +568,27 @@ serial_device_control(HANDLE handle, uint32 request, STREAM in, STREAM out)
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 = {
serial_create,
serial_close,

69
types.h
View File

@ -140,10 +140,73 @@ typedef uint32 HANDLE;
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(*read) (HANDLE handle, uint8 * data, uint32 length, uint32 * result);
NTSTATUS(*write) (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 offset, uint32 *result);
NTSTATUS (*device_control)(HANDLE handle, uint32 request, STREAM in, STREAM out);
}
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;