Ported over atftpd from Linux - should work on BSD as well, now.
Not yet tested, and it will probably not work correctly until we support IP_RECVDSTADDR correctly (we don't support that CMSG stuff yet). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22171 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
35f57d15ff
commit
ef27f7d0e0
@ -1,6 +1,7 @@
|
||||
SubDir HAIKU_TOP src bin network ;
|
||||
|
||||
SubInclude HAIKU_TOP src bin network arp ;
|
||||
SubInclude HAIKU_TOP src bin network atftpd ;
|
||||
SubInclude HAIKU_TOP src bin network ftp ;
|
||||
SubInclude HAIKU_TOP src bin network ifconfig ;
|
||||
SubInclude HAIKU_TOP src bin network login ;
|
||||
|
19
src/bin/network/atftpd/Jamfile
Normal file
19
src/bin/network/atftpd/Jamfile
Normal file
@ -0,0 +1,19 @@
|
||||
SubDir HAIKU_TOP src bin network atftpd ;
|
||||
|
||||
UseHeaders [ FDirName $(HAIKU_TOP) headers compatibility bsd ] : true ;
|
||||
|
||||
BinCommand atftpd :
|
||||
argz.c
|
||||
logger.c
|
||||
options.c
|
||||
stats.c
|
||||
tftp_def.c
|
||||
tftp_io.c
|
||||
tftpd.c
|
||||
tftpd_file.c
|
||||
tftpd_list.c
|
||||
tftpd_mcast.c
|
||||
tftpd_mtftp.c
|
||||
tftpd_pcre.c
|
||||
: libbsd.so $(TARGET_NETWORK_LIBS)
|
||||
;
|
340
src/bin/network/atftpd/LICENSE
Normal file
340
src/bin/network/atftpd/LICENSE
Normal file
@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
132
src/bin/network/atftpd/argz.c
Normal file
132
src/bin/network/atftpd/argz.c
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* argz.h
|
||||
* The functions below are "borrowed" from glibc-2.2.3 (argz-next.c).
|
||||
* This has been done to make atftp compile with uclibc, BSD, Solaris
|
||||
* and other platform without glic
|
||||
*
|
||||
*
|
||||
* $Id: argz.c,v 1.1 2003/01/21 01:38:35 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HAVE_ARGZ
|
||||
|
||||
/* Routines for dealing with '\0' separated arg vectors.
|
||||
Copyright (C) 1995, 96, 97, 98, 99, 2000 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "argz.h"
|
||||
|
||||
char * argz_next (const char *argz, size_t argz_len, const char *entry)
|
||||
{
|
||||
if (entry)
|
||||
{
|
||||
if (entry < argz + argz_len)
|
||||
entry = strchr (entry, '\0') + 1;
|
||||
|
||||
return entry >= argz + argz_len ? NULL : (char *) entry;
|
||||
}
|
||||
else
|
||||
if (argz_len > 0)
|
||||
return (char *) argz;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
error_t argz_create_sep (const char *string, int delim, char **argz, size_t *len)
|
||||
{
|
||||
size_t nlen = strlen (string) + 1;
|
||||
|
||||
if (nlen > 1)
|
||||
{
|
||||
const char *rp;
|
||||
char *wp;
|
||||
|
||||
*argz = (char *) malloc (nlen);
|
||||
if (*argz == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
rp = string;
|
||||
wp = *argz;
|
||||
do
|
||||
if (*rp == delim)
|
||||
{
|
||||
if (wp > *argz && wp[-1] != '\0')
|
||||
*wp++ = '\0';
|
||||
else
|
||||
--nlen;
|
||||
}
|
||||
else
|
||||
*wp++ = *rp;
|
||||
while (*rp++ != '\0');
|
||||
|
||||
if (nlen == 0)
|
||||
{
|
||||
free (*argz);
|
||||
*argz = NULL;
|
||||
*len = 0;
|
||||
}
|
||||
|
||||
*len = nlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
*argz = NULL;
|
||||
*len = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t argz_count (const char *argz, size_t len)
|
||||
{
|
||||
size_t count = 0;
|
||||
while (len > 0)
|
||||
{
|
||||
size_t part_len = strlen(argz);
|
||||
argz += part_len + 1;
|
||||
len -= part_len + 1;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Puts pointers to each string in ARGZ, plus a terminating 0 element, into
|
||||
ARGV, which must be large enough to hold them all. */
|
||||
void argz_extract (const char *argz, size_t len, char **argv)
|
||||
{
|
||||
while (len > 0)
|
||||
{
|
||||
size_t part_len = strlen (argz);
|
||||
*argv++ = (char *) argz;
|
||||
argz += part_len + 1;
|
||||
len -= part_len + 1;
|
||||
}
|
||||
*argv = 0;
|
||||
}
|
||||
|
||||
#endif
|
211
src/bin/network/atftpd/argz.h
Normal file
211
src/bin/network/atftpd/argz.h
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* argz.h
|
||||
* Source file borrowed from glibc-2.2.3. This has been done to make
|
||||
* atftp compile with uclibc, BSD, Solaris and other platform without
|
||||
* glic
|
||||
*
|
||||
* $Id: argz.h,v 1.1 2003/01/21 01:38:35 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Routines for dealing with '\0' separated arg vectors.
|
||||
Copyright (C) 1995, 96, 97, 98, 99, 2000 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA. */
|
||||
|
||||
#ifndef _ARGZ_H
|
||||
#define _ARGZ_H 1
|
||||
|
||||
//#include <features.h>
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#define __need_error_t
|
||||
#include <errno.h>
|
||||
#include <string.h> /* Need size_t, and strchr is called below. */
|
||||
|
||||
#ifndef __const
|
||||
# define __const const
|
||||
#endif
|
||||
|
||||
#ifndef __error_t_defined
|
||||
typedef int error_t;
|
||||
#endif
|
||||
|
||||
#define __THROW
|
||||
#define __restrict
|
||||
#define __attribute_pure__
|
||||
#define __BEGIN_DECLS
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/* Make a '\0' separated arg vector from a unix argv vector, returning it in
|
||||
ARGZ, and the total length in LEN. If a memory allocation error occurs,
|
||||
ENOMEM is returned, otherwise 0. The result can be destroyed using free. */
|
||||
extern error_t __argz_create (char *__const __argv[], char **__restrict __argz,
|
||||
size_t *__restrict __len) __THROW;
|
||||
extern error_t argz_create (char *__const __argv[], char **__restrict __argz,
|
||||
size_t *__restrict __len) __THROW;
|
||||
|
||||
/* Make a '\0' separated arg vector from a SEP separated list in
|
||||
STRING, returning it in ARGZ, and the total length in LEN. If a
|
||||
memory allocation error occurs, ENOMEM is returned, otherwise 0.
|
||||
The result can be destroyed using free. */
|
||||
extern error_t __argz_create_sep (__const char *__restrict __string,
|
||||
int __sep, char **__restrict __argz,
|
||||
size_t *__restrict __len) __THROW;
|
||||
extern error_t argz_create_sep (__const char *__restrict __string,
|
||||
int __sep, char **__restrict __argz,
|
||||
size_t *__restrict __len) __THROW;
|
||||
|
||||
/* Returns the number of strings in ARGZ. */
|
||||
extern size_t __argz_count (__const char *__argz, size_t __len)
|
||||
__THROW __attribute_pure__;
|
||||
extern size_t argz_count (__const char *__argz, size_t __len)
|
||||
__THROW __attribute_pure__;
|
||||
|
||||
/* Puts pointers to each string in ARGZ into ARGV, which must be large enough
|
||||
to hold them all. */
|
||||
extern void __argz_extract (__const char *__restrict __argz, size_t __len,
|
||||
char **__restrict __argv) __THROW;
|
||||
extern void argz_extract (__const char *__restrict __argz, size_t __len,
|
||||
char **__restrict __argv) __THROW;
|
||||
|
||||
/* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
|
||||
except the last into the character SEP. */
|
||||
extern void __argz_stringify (char *__argz, size_t __len, int __sep) __THROW;
|
||||
extern void argz_stringify (char *__argz, size_t __len, int __sep) __THROW;
|
||||
|
||||
/* Append BUF, of length BUF_LEN to the argz vector in ARGZ & ARGZ_LEN. */
|
||||
extern error_t __argz_append (char **__restrict __argz,
|
||||
size_t *__restrict __argz_len,
|
||||
__const char *__restrict __buf, size_t _buf_len)
|
||||
__THROW;
|
||||
extern error_t argz_append (char **__restrict __argz,
|
||||
size_t *__restrict __argz_len,
|
||||
__const char *__restrict __buf, size_t __buf_len)
|
||||
__THROW;
|
||||
|
||||
/* Append STR to the argz vector in ARGZ & ARGZ_LEN. */
|
||||
extern error_t __argz_add (char **__restrict __argz,
|
||||
size_t *__restrict __argz_len,
|
||||
__const char *__restrict __str) __THROW;
|
||||
extern error_t argz_add (char **__restrict __argz,
|
||||
size_t *__restrict __argz_len,
|
||||
__const char *__restrict __str) __THROW;
|
||||
|
||||
/* Append SEP separated list in STRING to the argz vector in ARGZ &
|
||||
ARGZ_LEN. */
|
||||
extern error_t __argz_add_sep (char **__restrict __argz,
|
||||
size_t *__restrict __argz_len,
|
||||
__const char *__restrict __string, int __delim)
|
||||
__THROW;
|
||||
extern error_t argz_add_sep (char **__restrict __argz,
|
||||
size_t *__restrict __argz_len,
|
||||
__const char *__restrict __string, int __delim)
|
||||
__THROW;
|
||||
|
||||
/* Delete ENTRY from ARGZ & ARGZ_LEN, if it appears there. */
|
||||
extern void __argz_delete (char **__restrict __argz,
|
||||
size_t *__restrict __argz_len,
|
||||
char *__restrict __entry) __THROW;
|
||||
extern void argz_delete (char **__restrict __argz,
|
||||
size_t *__restrict __argz_len,
|
||||
char *__restrict __entry) __THROW;
|
||||
|
||||
/* Insert ENTRY into ARGZ & ARGZ_LEN before BEFORE, which should be an
|
||||
existing entry in ARGZ; if BEFORE is NULL, ENTRY is appended to the end.
|
||||
Since ARGZ's first entry is the same as ARGZ, argz_insert (ARGZ, ARGZ_LEN,
|
||||
ARGZ, ENTRY) will insert ENTRY at the beginning of ARGZ. If BEFORE is not
|
||||
in ARGZ, EINVAL is returned, else if memory can't be allocated for the new
|
||||
ARGZ, ENOMEM is returned, else 0. */
|
||||
extern error_t __argz_insert (char **__restrict __argz,
|
||||
size_t *__restrict __argz_len,
|
||||
char *__restrict __before,
|
||||
__const char *__restrict __entry) __THROW;
|
||||
extern error_t argz_insert (char **__restrict __argz,
|
||||
size_t *__restrict __argz_len,
|
||||
char *__restrict __before,
|
||||
__const char *__restrict __entry) __THROW;
|
||||
|
||||
/* Replace any occurrences of the string STR in ARGZ with WITH, reallocating
|
||||
ARGZ as necessary. If REPLACE_COUNT is non-zero, *REPLACE_COUNT will be
|
||||
incremented by number of replacements performed. */
|
||||
extern error_t __argz_replace (char **__restrict __argz,
|
||||
size_t *__restrict __argz_len,
|
||||
__const char *__restrict __str,
|
||||
__const char *__restrict __with,
|
||||
unsigned int *__restrict __replace_count);
|
||||
extern error_t argz_replace (char **__restrict __argz,
|
||||
size_t *__restrict __argz_len,
|
||||
__const char *__restrict __str,
|
||||
__const char *__restrict __with,
|
||||
unsigned int *__restrict __replace_count);
|
||||
|
||||
/* Returns the next entry in ARGZ & ARGZ_LEN after ENTRY, or NULL if there
|
||||
are no more. If entry is NULL, then the first entry is returned. This
|
||||
behavior allows two convenient iteration styles:
|
||||
|
||||
char *entry = 0;
|
||||
while ((entry = argz_next (argz, argz_len, entry)))
|
||||
...;
|
||||
|
||||
or
|
||||
|
||||
char *entry;
|
||||
for (entry = argz; entry; entry = argz_next (argz, argz_len, entry))
|
||||
...;
|
||||
*/
|
||||
extern char *__argz_next (__const char *__restrict __argz, size_t __argz_len,
|
||||
__const char *__restrict __entry) __THROW;
|
||||
extern char *argz_next (__const char *__restrict __argz, size_t __argz_len,
|
||||
__const char *__restrict __entry) __THROW;
|
||||
|
||||
#ifdef __USE_EXTERN_INLINES
|
||||
extern inline char *
|
||||
__argz_next (__const char *__argz, size_t __argz_len,
|
||||
__const char *__entry) __THROW
|
||||
{
|
||||
if (__entry)
|
||||
{
|
||||
if (__entry < __argz + __argz_len)
|
||||
__entry = strchr (__entry, '\0') + 1;
|
||||
|
||||
return __entry >= __argz + __argz_len ? (char *) NULL : (char *) __entry;
|
||||
}
|
||||
else
|
||||
return __argz_len > 0 ? (char *) __argz : 0;
|
||||
}
|
||||
extern inline char *
|
||||
argz_next (__const char *__argz, size_t __argz_len,
|
||||
__const char *__entry) __THROW
|
||||
{
|
||||
return __argz_next (__argz, __argz_len, __entry);
|
||||
}
|
||||
#endif /* Use extern inlines. */
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* argz.h */
|
7
src/bin/network/atftpd/config.h
Normal file
7
src/bin/network/atftpd/config.h
Normal file
@ -0,0 +1,7 @@
|
||||
#include <pthread.h>
|
||||
|
||||
#define HAVE_MTFTP 1
|
||||
//#undef HAVE_PCRE
|
||||
//#undef HAVE_WRAP
|
||||
|
||||
#define VERSION "0.7"
|
137
src/bin/network/atftpd/logger.c
Normal file
137
src/bin/network/atftpd/logger.c
Normal file
@ -0,0 +1,137 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* logger.c
|
||||
* functions for logging messages.
|
||||
*
|
||||
* $Id: logger.c,v 1.12 2004/02/27 02:05:26 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include "logger.h"
|
||||
|
||||
#define MAXLEN 128
|
||||
|
||||
static int log_syslog_is_open = 0;
|
||||
static int log_priority = 0;
|
||||
static char *log_filename = NULL;
|
||||
static int log_fd;
|
||||
static FILE *log_fp = NULL;
|
||||
static char *log_ident;
|
||||
|
||||
/*
|
||||
* Open a file for logging. If filename is NULL, then use
|
||||
* stderr for the client or the syslog for the server. Log
|
||||
* only message less or equal to priority.
|
||||
*/
|
||||
void open_logger(char *ident, char *filename, int priority)
|
||||
{
|
||||
close_logger(); /* make sure we initialise variables and close
|
||||
previously opened log. */
|
||||
|
||||
log_priority = priority;
|
||||
|
||||
if (ident)
|
||||
log_ident = strdup(ident);
|
||||
else
|
||||
log_ident = "unset";
|
||||
|
||||
if (filename)
|
||||
log_filename = strdup(filename);
|
||||
else
|
||||
{
|
||||
openlog(log_ident, LOG_PID, LOG_DAEMON);
|
||||
log_syslog_is_open = 1;
|
||||
}
|
||||
|
||||
if (log_filename)
|
||||
{
|
||||
if ((log_fd = open(log_filename, O_WRONLY | O_APPEND)) < 0)
|
||||
{
|
||||
openlog(log_ident, LOG_PID, LOG_DAEMON);
|
||||
log_syslog_is_open = 1;
|
||||
logger(LOG_CRIT, "Unable to open %s for logging, "
|
||||
"reverting to syslog", log_filename);
|
||||
}
|
||||
else
|
||||
log_fp = fdopen(log_fd, "a");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Same as syslog but allow to format a string, like printf, when logging to
|
||||
* file. This fonction will either call syslog or fprintf depending of the
|
||||
* previous call to open_logger().
|
||||
*/
|
||||
void logger(int severity, const char *fmt, ...)
|
||||
{
|
||||
char message[MAXLEN];
|
||||
char time_buf[MAXLEN];
|
||||
char hostname[MAXLEN];
|
||||
time_t t;
|
||||
struct tm *tm;
|
||||
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
time(&t);
|
||||
tm = localtime(&t);
|
||||
strftime(time_buf, MAXLEN, "%b %d %H:%M:%S", tm);
|
||||
gethostname(hostname, MAXLEN);
|
||||
|
||||
if (severity <= log_priority)
|
||||
{
|
||||
vsnprintf(message, sizeof(message), fmt, args);
|
||||
|
||||
if (log_fp)
|
||||
{
|
||||
fprintf(log_fp, "%s %s %s[%d.%d]: %s\n", time_buf, hostname,
|
||||
log_ident, getpid(), pthread_self(), message);
|
||||
fflush(log_fp);
|
||||
}
|
||||
else if (log_syslog_is_open)
|
||||
syslog(severity, "%s", message);
|
||||
else
|
||||
fprintf(stderr, "%s %s %s[%d.%d]: %s\n", time_buf, hostname,
|
||||
log_ident, getpid(), pthread_self(), message);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the file or syslog. Initialise variables.
|
||||
*/
|
||||
void close_logger(void)
|
||||
{
|
||||
log_priority = 0;
|
||||
if (log_syslog_is_open)
|
||||
closelog();
|
||||
log_syslog_is_open = 0;
|
||||
if (log_fp)
|
||||
fclose(log_fp);
|
||||
log_fp = NULL;
|
||||
if (log_filename)
|
||||
free(log_filename);
|
||||
log_filename = NULL;
|
||||
if (log_ident)
|
||||
free(log_ident);
|
||||
}
|
26
src/bin/network/atftpd/logger.h
Normal file
26
src/bin/network/atftpd/logger.h
Normal file
@ -0,0 +1,26 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* logger.h
|
||||
*
|
||||
* $Id: logger.h,v 1.6 2000/12/27 00:57:16 remi Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef logger_h
|
||||
#define logger_h
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
void open_logger(char *ident, char *filename, int priority);
|
||||
void logger(int severity, const char *fmt, ...);
|
||||
void close_logger(void);
|
||||
|
||||
#endif
|
356
src/bin/network/atftpd/options.c
Normal file
356
src/bin/network/atftpd/options.c
Normal file
@ -0,0 +1,356 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* options.c
|
||||
* Set of functions to deal with the options structure and for parsing
|
||||
* options in TFTP data buffer.
|
||||
*
|
||||
* $Id: options.c,v 1.16 2003/04/25 00:16:18 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#if HAVE_ARGZ
|
||||
#include <argz.h>
|
||||
#else
|
||||
#include "argz.h"
|
||||
#endif
|
||||
|
||||
#include <arpa/tftp.h>
|
||||
#include <string.h>
|
||||
#include "options.h"
|
||||
|
||||
/*
|
||||
* Fill a structure with the request packet of the client.
|
||||
*/
|
||||
int opt_parse_request(char *data, int data_size, struct tftp_opt *options)
|
||||
{
|
||||
char *entry = NULL;
|
||||
char *tmp;
|
||||
struct tftphdr *tftp_data = (struct tftphdr *)data;
|
||||
size_t size = data_size - sizeof(tftp_data->th_opcode);
|
||||
|
||||
/* read filename */
|
||||
entry = argz_next(tftp_data->th_stuff, size, entry);
|
||||
if (!entry)
|
||||
return ERR;
|
||||
else
|
||||
opt_set_options(options, "filename", entry);
|
||||
/* read mode */
|
||||
entry = argz_next(tftp_data->th_stuff, size, entry);
|
||||
if (!entry)
|
||||
return ERR;
|
||||
else
|
||||
opt_set_options(options, "mode", entry);
|
||||
/* scan for options */
|
||||
// FIXME: we should use opt_parse_options() here
|
||||
while ((entry = argz_next(tftp_data->th_stuff, size, entry)))
|
||||
{
|
||||
tmp = entry;
|
||||
entry = argz_next(tftp_data->th_stuff, size, entry);
|
||||
if (!entry)
|
||||
return ERR;
|
||||
else
|
||||
opt_set_options(options, tmp, entry);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill a structure looking only at TFTP options.
|
||||
*/
|
||||
int opt_parse_options(char *data, int data_size, struct tftp_opt *options)
|
||||
{
|
||||
char *entry = NULL;
|
||||
char *tmp;
|
||||
struct tftphdr *tftp_data = (struct tftphdr *)data;
|
||||
size_t size = data_size - sizeof(tftp_data->th_opcode);
|
||||
|
||||
while ((entry = argz_next(tftp_data->th_stuff, size, entry)))
|
||||
{
|
||||
tmp = entry;
|
||||
entry = argz_next(tftp_data->th_stuff, size, entry);
|
||||
if (!entry)
|
||||
return ERR;
|
||||
else
|
||||
opt_set_options(options, tmp, entry);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set an option by name in the structure.
|
||||
* name is the name of the option as in tftp_def.c.
|
||||
* name is it's new value, that must comply with the rfc's.
|
||||
* When setting an option, it is marked as specified.
|
||||
*
|
||||
*/
|
||||
int opt_set_options(struct tftp_opt *options, char *name, char *value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < OPT_NUMBER; i++)
|
||||
{
|
||||
if (strncasecmp(name, options[i].option, OPT_SIZE) == 0)
|
||||
{
|
||||
options[i].specified = 1;
|
||||
if (value)
|
||||
Strncpy(options[i].value, value, VAL_SIZE);
|
||||
else
|
||||
Strncpy(options[i].value, tftp_default_options[i].value,
|
||||
VAL_SIZE);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
return ERR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return "value" for a given option name in the given option
|
||||
* structure.
|
||||
*/
|
||||
int opt_get_options(struct tftp_opt *options, char *name, char *value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < OPT_NUMBER; i++)
|
||||
{
|
||||
if (strncasecmp(name, options[i].option, OPT_SIZE) == 0)
|
||||
{
|
||||
if (options[i].enabled)
|
||||
Strncpy(value, options[i].value, VAL_SIZE);
|
||||
else
|
||||
return ERR;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
return ERR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable an option by name.
|
||||
*/
|
||||
int opt_disable_options(struct tftp_opt *options, char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 2; i < OPT_NUMBER; i++)
|
||||
{
|
||||
if (name == NULL)
|
||||
options[i].specified = 0;
|
||||
else
|
||||
{
|
||||
if (strncasecmp(name, options[i].option, OPT_SIZE) == 0)
|
||||
{
|
||||
options[i].specified = 0;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name == NULL)
|
||||
return OK;
|
||||
return ERR;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return 1 if one or more options are specified in the options structure.
|
||||
*/
|
||||
int opt_support_options(struct tftp_opt *options)
|
||||
{
|
||||
int i;
|
||||
int support = 0;
|
||||
|
||||
for (i = 2; i < OPT_NUMBER; i++)
|
||||
{
|
||||
if (options[i].specified)
|
||||
support = 1;
|
||||
}
|
||||
return support;
|
||||
}
|
||||
|
||||
/*
|
||||
* The next few functions deal with TFTP options. Function's name are self
|
||||
* explicative.
|
||||
*/
|
||||
|
||||
int opt_get_tsize(struct tftp_opt *options)
|
||||
{
|
||||
int tsize;
|
||||
if (options[OPT_TSIZE].enabled && options[OPT_TSIZE].specified)
|
||||
{
|
||||
tsize = atoi(options[OPT_TSIZE].value);
|
||||
return tsize;
|
||||
}
|
||||
return ERR;
|
||||
}
|
||||
|
||||
int opt_get_timeout(struct tftp_opt *options)
|
||||
{
|
||||
int timeout;
|
||||
if (options[OPT_TIMEOUT].enabled && options[OPT_TIMEOUT].specified)
|
||||
{
|
||||
timeout = atoi(options[OPT_TIMEOUT].value);
|
||||
return timeout;
|
||||
}
|
||||
return ERR;
|
||||
}
|
||||
|
||||
int opt_get_blksize(struct tftp_opt *options)
|
||||
{
|
||||
int blksize;
|
||||
if (options[OPT_BLKSIZE].enabled && options[OPT_BLKSIZE].specified)
|
||||
{
|
||||
blksize = atoi(options[OPT_BLKSIZE].value);
|
||||
return blksize;
|
||||
}
|
||||
return ERR;
|
||||
}
|
||||
|
||||
int opt_get_multicast(struct tftp_opt *options, char *addr, int *port, int *mc)
|
||||
{
|
||||
char *token = NULL;
|
||||
char *string = NULL;
|
||||
char *temp = NULL;
|
||||
|
||||
if (options[OPT_MULTICAST].enabled && options[OPT_MULTICAST].specified)
|
||||
{
|
||||
string = strdup(options[OPT_MULTICAST].value);
|
||||
/* get first argument */
|
||||
if ((token = strtok_r(string, ",", &temp)) == NULL)
|
||||
{
|
||||
free(string);
|
||||
return ERR;
|
||||
}
|
||||
else
|
||||
Strncpy(addr, token, IPADDRLEN);
|
||||
/* get second argument */
|
||||
if ((token = strtok_r(NULL, ",", &temp)) == NULL)
|
||||
{
|
||||
free(string);
|
||||
return ERR;
|
||||
}
|
||||
else
|
||||
{
|
||||
*port = atoi(token);
|
||||
if ((*port < 0) || (*port > 65536))
|
||||
{
|
||||
free(string);
|
||||
return ERR;
|
||||
}
|
||||
}
|
||||
/* get third (last) argument */
|
||||
if ((token = strtok_r(NULL, ",", &temp)) == NULL)
|
||||
{
|
||||
free(string);
|
||||
return ERR;
|
||||
}
|
||||
else
|
||||
{
|
||||
*mc = atoi(token);
|
||||
if ((*mc != 0) && (*mc != 1))
|
||||
{
|
||||
free(string);
|
||||
return ERR;
|
||||
}
|
||||
}
|
||||
free(string);
|
||||
return *mc;
|
||||
}
|
||||
return ERR;
|
||||
}
|
||||
|
||||
void opt_set_tsize(int tsize, struct tftp_opt *options)
|
||||
{
|
||||
snprintf(options[OPT_TSIZE].value, VAL_SIZE, "%d", tsize);
|
||||
}
|
||||
|
||||
void opt_set_timeout(int timeout, struct tftp_opt *options)
|
||||
{
|
||||
snprintf(options[OPT_TIMEOUT].value, VAL_SIZE, "%d", timeout);
|
||||
}
|
||||
|
||||
void opt_set_blksize(int blksize, struct tftp_opt *options)
|
||||
{
|
||||
snprintf(options[OPT_BLKSIZE].value, VAL_SIZE, "%d", blksize);
|
||||
}
|
||||
|
||||
void opt_set_multicast(struct tftp_opt *options, char *addr, int port, int mc)
|
||||
{
|
||||
snprintf(options[OPT_MULTICAST].value, VAL_SIZE, "%s,%d,%d", addr, port,
|
||||
mc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Format the content of the options structure (this is the content of a
|
||||
* read or write request) to a string.
|
||||
*/
|
||||
void opt_request_to_string(struct tftp_opt *options, char *string, int len)
|
||||
{
|
||||
int i, index = 0;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
if ((index + strlen(options[i].option) + 2) < len)
|
||||
{
|
||||
Strncpy(string + index, options[i].option, len - index);
|
||||
index += strlen(options[i].option);
|
||||
Strncpy(string + index, ": ", len - index);
|
||||
index += 2;
|
||||
}
|
||||
if ((index + strlen(options[i].value) + 2) < len)
|
||||
{
|
||||
Strncpy(string + index, options[i].value, len - index);
|
||||
index += strlen(options[i].value);
|
||||
Strncpy(string + index, ", ", len - index);
|
||||
index += 2;
|
||||
}
|
||||
}
|
||||
opt_options_to_string(options, string + index, len - index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the options structure to a string.
|
||||
*/
|
||||
void opt_options_to_string(struct tftp_opt *options, char *string, int len)
|
||||
{
|
||||
int i, index = 0;
|
||||
|
||||
for (i = 2; i < OPT_NUMBER; i++)
|
||||
{
|
||||
if (options[i].specified && options[i].enabled)
|
||||
{
|
||||
if ((index + strlen(options[i].option) + 2) < len)
|
||||
{
|
||||
Strncpy(string + index, options[i].option, len - index);
|
||||
index += strlen(options[i].option);
|
||||
Strncpy(string + index, ": ", len - index);
|
||||
index += 2;
|
||||
}
|
||||
if ((index + strlen(options[i].value) + 2) < len)
|
||||
{
|
||||
Strncpy(string + index, options[i].value, len - index);
|
||||
index += strlen(options[i].value);
|
||||
Strncpy(string + index, ", ", len - index);
|
||||
index += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index > 0)
|
||||
string[index - 2] = 0;
|
||||
else
|
||||
string[0] = 0;
|
||||
}
|
50
src/bin/network/atftpd/options.h
Normal file
50
src/bin/network/atftpd/options.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* options.h
|
||||
*
|
||||
* $Id: options.h,v 1.7 2001/07/06 23:35:18 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef options_h
|
||||
#define options_h
|
||||
|
||||
#include "tftp_def.h"
|
||||
|
||||
/* Structure definition for tftp options. */
|
||||
struct tftp_opt {
|
||||
char option[OPT_SIZE];
|
||||
char value[VAL_SIZE];
|
||||
int specified; /* specified by the client (for tftp server) */
|
||||
int enabled; /* enabled for use by server or client */
|
||||
};
|
||||
|
||||
extern struct tftp_opt tftp_default_options[OPT_NUMBER];
|
||||
|
||||
int opt_parse_request(char *data, int data_size, struct tftp_opt *options);
|
||||
int opt_parse_options(char *data, int data_size, struct tftp_opt *options);
|
||||
int opt_set_options(struct tftp_opt *options, char *name, char *value);
|
||||
int opt_get_options(struct tftp_opt *options, char *name, char *value);
|
||||
int opt_disable_options(struct tftp_opt *options, char *name);
|
||||
int opt_support_options(struct tftp_opt *options);
|
||||
int opt_get_tsize(struct tftp_opt *options);
|
||||
int opt_get_timeout(struct tftp_opt *options);
|
||||
int opt_get_blksize(struct tftp_opt *options);
|
||||
int opt_get_multicast(struct tftp_opt *options, char *addr, int *port, int *mc);
|
||||
void opt_set_tsize(int tsize, struct tftp_opt *options);
|
||||
void opt_set_timeout(int timeout, struct tftp_opt *options);
|
||||
void opt_set_blksize(int blksize, struct tftp_opt *options);
|
||||
void opt_set_multicast(struct tftp_opt *options, char *addr, int port, int mc);
|
||||
void opt_request_to_string(struct tftp_opt *options, char *string, int len);
|
||||
void opt_options_to_string(struct tftp_opt *options, char *string, int len);
|
||||
|
||||
#endif
|
||||
|
180
src/bin/network/atftpd/stats.c
Normal file
180
src/bin/network/atftpd/stats.c
Normal file
@ -0,0 +1,180 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* stats.c
|
||||
* some functions to collect statistics
|
||||
*
|
||||
* $Id: stats.c,v 1.6 2002/03/27 03:02:12 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include "tftp_def.h"
|
||||
#include "stats.h"
|
||||
#include "logger.h"
|
||||
|
||||
/*
|
||||
* That structure allows global statistic to be collected. See stats.h.
|
||||
*/
|
||||
struct server_stats s_stats;
|
||||
|
||||
/*
|
||||
* Must be called to initilise stats structure and record the
|
||||
* start time.
|
||||
*/
|
||||
void stats_start(void)
|
||||
{
|
||||
/* stats struct initialisation */
|
||||
memset(&s_stats, 0, sizeof(s_stats));
|
||||
s_stats.min_time.tv_sec = LONG_MAX;
|
||||
pthread_mutex_init(&s_stats.mutex, NULL);
|
||||
|
||||
gettimeofday(&s_stats.start_time, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when execution is finnished, before calling stats_print.
|
||||
*/
|
||||
void stats_end(void)
|
||||
{
|
||||
gettimeofday(&s_stats.end_time, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by server threads each time a file is sent succesfully.
|
||||
* Be aware that a mutex is locked in there.
|
||||
*/
|
||||
void stats_send_locked(void)
|
||||
{
|
||||
pthread_mutex_lock(&s_stats.mutex);
|
||||
s_stats.number_of_server++;
|
||||
s_stats.num_file_send++;
|
||||
pthread_mutex_unlock(&s_stats.mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by verver threads each time a file is received.
|
||||
*/
|
||||
void stats_recv_locked(void)
|
||||
{
|
||||
pthread_mutex_lock(&s_stats.mutex);
|
||||
s_stats.number_of_server++;
|
||||
s_stats.num_file_recv++;
|
||||
pthread_mutex_unlock(&s_stats.mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by server threads each time tftpd_send_file or tftpd_recv_file
|
||||
* return with error.
|
||||
*/
|
||||
void stats_err_locked(void)
|
||||
{
|
||||
pthread_mutex_lock(&s_stats.mutex);
|
||||
s_stats.number_of_err++;
|
||||
pthread_mutex_unlock(&s_stats.mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by server threads when the maximum number of threads is reached.
|
||||
*/
|
||||
void stats_abort_locked(void)
|
||||
{
|
||||
pthread_mutex_lock(&s_stats.mutex);
|
||||
s_stats.number_of_abort++;
|
||||
pthread_mutex_unlock(&s_stats.mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by main thread only (in fact in tftpd_receive_request(), but
|
||||
* before stdin_mutex is released) every time a new thread is created.
|
||||
* We record the number of thread, the number of simultaeous thread, the
|
||||
* between threads.
|
||||
*/
|
||||
void stats_new_thread(int number_of_thread)
|
||||
{
|
||||
struct timeval tmp;
|
||||
|
||||
if (number_of_thread > s_stats.max_simul_threads)
|
||||
s_stats.max_simul_threads = number_of_thread;
|
||||
|
||||
/* calculate the arrival time of this thread */
|
||||
if (s_stats.prev_time.tv_sec != 0)
|
||||
{
|
||||
gettimeofday(&s_stats.curr_time, NULL);
|
||||
timeval_diff(&s_stats.diff_time, &s_stats.curr_time,
|
||||
&s_stats.prev_time);
|
||||
if (timeval_diff(&tmp, &s_stats.diff_time,
|
||||
&s_stats.min_time) < 0)
|
||||
memcpy(&s_stats.min_time, &s_stats.diff_time,
|
||||
sizeof(struct timeval));
|
||||
if (timeval_diff(&tmp, &s_stats.diff_time,
|
||||
&s_stats.max_time) > 0)
|
||||
memcpy(&s_stats.max_time, &s_stats.diff_time,
|
||||
sizeof(struct timeval));
|
||||
memcpy(&s_stats.prev_time, &s_stats.curr_time,
|
||||
sizeof(struct timeval));
|
||||
}
|
||||
else
|
||||
gettimeofday(&s_stats.prev_time, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by server threads when the finnished to add CPU ressources
|
||||
* information.
|
||||
*/
|
||||
void stats_thread_usage_locked(void)
|
||||
{
|
||||
struct tms tms_tmp;
|
||||
|
||||
times(&tms_tmp);
|
||||
pthread_mutex_lock(&s_stats.mutex);
|
||||
s_stats.tms_thread.tms_utime += tms_tmp.tms_utime;
|
||||
s_stats.tms_thread.tms_stime += tms_tmp.tms_stime;
|
||||
pthread_mutex_unlock(&s_stats.mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called at the end of the main thread, when no other threads are
|
||||
* running, to print the final statistics.
|
||||
*/
|
||||
void stats_print(void)
|
||||
{
|
||||
struct timeval tmp;
|
||||
|
||||
timeval_diff(&tmp, &s_stats.end_time, &s_stats.start_time);
|
||||
times(&s_stats.tms);
|
||||
s_stats.tms.tms_utime += s_stats.tms_thread.tms_utime;
|
||||
s_stats.tms.tms_stime += s_stats.tms_thread.tms_stime;
|
||||
|
||||
logger(LOG_INFO, " Load measurements:");
|
||||
logger(LOG_INFO, " User: %8.3fs Sys:%8.3fs",
|
||||
(double)(s_stats.tms.tms_utime) / CLK_TCK,
|
||||
(double)(s_stats.tms.tms_stime) / CLK_TCK);
|
||||
logger(LOG_INFO, " Total:%8.3fs CPU:%8.3f%%",
|
||||
(double)(tmp.tv_sec + tmp.tv_usec * 1e-6),
|
||||
(double)(s_stats.tms.tms_utime + s_stats.tms.tms_stime) /
|
||||
(double)(tmp.tv_sec + tmp.tv_usec * 1e-6));
|
||||
logger(LOG_INFO, " Time between connections:");
|
||||
if (s_stats.min_time.tv_sec == LONG_MAX)
|
||||
logger(LOG_INFO, " Min: ----- Max: -----");
|
||||
else
|
||||
logger(LOG_INFO, " Min: %.3fs Max: %.3fs",
|
||||
(double)(s_stats.min_time.tv_sec + s_stats.min_time.tv_usec * 1e-6),
|
||||
(double)(s_stats.max_time.tv_sec + s_stats.max_time.tv_usec * 1e-6));
|
||||
logger(LOG_INFO, " Thread stats:");
|
||||
logger(LOG_INFO, " simultaneous threads: %d", s_stats.max_simul_threads);
|
||||
logger(LOG_INFO, " number of servers: %d", s_stats.number_of_server);
|
||||
logger(LOG_INFO, " number of aborts: %d", s_stats.number_of_abort);
|
||||
logger(LOG_INFO, " number of errors: %d", s_stats.number_of_err);
|
||||
logger(LOG_INFO, " number of files sent: %d", s_stats.num_file_send);
|
||||
logger(LOG_INFO, " number of files received: %d", s_stats.num_file_recv);
|
||||
}
|
62
src/bin/network/atftpd/stats.h
Normal file
62
src/bin/network/atftpd/stats.h
Normal file
@ -0,0 +1,62 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* stats.h
|
||||
*
|
||||
* $Id: stats.h,v 1.3 2000/12/27 17:02:23 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef stats_h
|
||||
#define stats_h
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/times.h>
|
||||
|
||||
/* structure to collect some stats */
|
||||
struct server_stats {
|
||||
/* updated by main thread */
|
||||
struct timeval start_time;
|
||||
struct timeval end_time;
|
||||
struct tms tms;
|
||||
|
||||
/* connection statistics, updated by main thread */
|
||||
int max_simul_threads; /* maximum number of simultaneous server */
|
||||
struct timeval min_time; /* time between connection stats */
|
||||
struct timeval max_time;
|
||||
struct timeval curr_time; /* temporary usage for calculation */
|
||||
struct timeval prev_time;
|
||||
struct timeval diff_time;
|
||||
|
||||
/* updated by server thread */
|
||||
pthread_mutex_t mutex;
|
||||
struct tms tms_thread;
|
||||
int number_of_server; /* number of server that return successfully */
|
||||
int number_of_abort; /* when number max of client is reached */
|
||||
int number_of_err; /* send or receive that return with error */
|
||||
int num_file_send;
|
||||
int num_file_recv;
|
||||
int byte_send; /* total byte transfered to client (file) */
|
||||
int byte_recv; /* total byte read from client (file) */
|
||||
};
|
||||
|
||||
/* Functions defined in stats.c */
|
||||
void stats_start(void);
|
||||
void stats_end(void);
|
||||
void stats_send_locked(void);
|
||||
void stats_recv_locked(void);
|
||||
void stats_err_locked(void);
|
||||
void stats_abort_locked(void);
|
||||
void stats_new_thread(int number_of_thread);
|
||||
void stats_thread_usage_locked(void);
|
||||
void stats_print(void);
|
||||
|
||||
#endif
|
183
src/bin/network/atftpd/tftp_def.c
Normal file
183
src/bin/network/atftpd/tftp_def.c
Normal file
@ -0,0 +1,183 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* tftp_def.c
|
||||
*
|
||||
* $Id: tftp_def.c,v 1.15 2004/02/13 03:16:09 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "tftp_def.h"
|
||||
#include "options.h"
|
||||
#include "logger.h"
|
||||
|
||||
/*
|
||||
* This is the default option structure, that must be used
|
||||
* for initialisation.
|
||||
*/
|
||||
|
||||
// FIXME: is there a way to use TIMEOUT and SEGSIZE here?
|
||||
struct tftp_opt tftp_default_options[OPT_NUMBER] = {
|
||||
{ "filename", "", 0, 1}, /* file to transfer */
|
||||
{ "mode", "octet", 0, 1}, /* mode for transfer */
|
||||
{ "tsize", "0", 0, 1 }, /* RFC1350 options. See RFC2347, */
|
||||
{ "timeout", "5", 0, 1 }, /* 2348, 2349, 2090. */
|
||||
{ "blksize", "512", 0, 1 }, /* This is the default option */
|
||||
{ "multicast", "", 0, 1 }, /* structure */
|
||||
{ "", "", 0, 0}
|
||||
};
|
||||
|
||||
/* Error message defined in RFC1350. */
|
||||
char *tftp_errmsg[9] = {
|
||||
"Undefined error code",
|
||||
"File not found",
|
||||
"Access violation",
|
||||
"Disk full or allocation exceeded",
|
||||
"Illegal TFTP operation",
|
||||
"Unknown transfer ID",
|
||||
"File already exists",
|
||||
"No such user",
|
||||
"Failure to negotiate RFC1782 options",
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Compute the difference of two timeval structs handling wrap around.
|
||||
* The result is retruned in *res.
|
||||
* Return value are:
|
||||
* 1 if t1 > t0
|
||||
* 0 if t1 = t0
|
||||
* -1 if t1 < t0
|
||||
*/
|
||||
int timeval_diff(struct timeval *res, struct timeval *t1, struct timeval *t0)
|
||||
{
|
||||
res->tv_sec = t1->tv_sec - t0->tv_sec;
|
||||
res->tv_usec = t1->tv_usec - t0->tv_usec;
|
||||
|
||||
if (res->tv_sec > 0)
|
||||
{
|
||||
if (res->tv_usec >= 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
res->tv_sec -= 1;
|
||||
res->tv_usec += 1000000;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (res->tv_sec < 0)
|
||||
{
|
||||
if (res->tv_usec > 0)
|
||||
{
|
||||
res->tv_sec += 1;
|
||||
res->tv_usec -= 1000000;
|
||||
return -1;
|
||||
}
|
||||
else if (res->tv_usec <= 0);
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (res->tv_usec > 0)
|
||||
return 1;
|
||||
else if (res->tv_usec < 0)
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a string in engineering notation.
|
||||
*
|
||||
* IN:
|
||||
* value: value to print
|
||||
* string: if NULL, the function print to stdout, else if print
|
||||
* to the string.
|
||||
* format: format string for printf.
|
||||
*/
|
||||
int print_eng(double value, char *string, int size, char *format)
|
||||
{
|
||||
char suffix[] = {'f', 'p', 'n', 'u', 'm', 0, 'k', 'M', 'G', 'T', 'P'};
|
||||
double tmp;
|
||||
double div = 1e-15;
|
||||
int i;
|
||||
|
||||
|
||||
for (i = 0; i < 11; i++)
|
||||
{
|
||||
tmp = value / div;
|
||||
if ((tmp > 1.0) && (tmp < 1000.0))
|
||||
break;
|
||||
div *= 1000.0;
|
||||
}
|
||||
if (string)
|
||||
snprintf(string, size, format, tmp, suffix[i]);
|
||||
else
|
||||
printf(format, tmp, suffix[i]);
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a strncpy function that take care of string NULL termination
|
||||
*/
|
||||
inline char *Strncpy(char *to, const char *from, size_t size)
|
||||
{
|
||||
to[size-1] = '\000';
|
||||
return strncpy(to, from, size - 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* gethostbyname replacement that is reentrant. This function is copyied
|
||||
* from the libc manual.
|
||||
*/
|
||||
int Gethostbyname(char *addr, struct hostent *host)
|
||||
{
|
||||
struct hostent *hp;
|
||||
char *tmpbuf;
|
||||
size_t tmpbuflen;
|
||||
int herr;
|
||||
|
||||
tmpbuflen = 1024;
|
||||
|
||||
if ((tmpbuf = (char *)malloc(tmpbuflen)) == NULL)
|
||||
return ERR;
|
||||
|
||||
hp = gethostbyname_r(addr, host, tmpbuf, tmpbuflen, &herr);
|
||||
|
||||
free(tmpbuf);
|
||||
|
||||
/* Check for errors. */
|
||||
if (hp == NULL)
|
||||
{
|
||||
logger(LOG_ERR, "%s: %d: gethostbyname_r: %s",
|
||||
__FILE__, __LINE__, strerror(herr));
|
||||
return ERR;
|
||||
}
|
||||
if (hp != host)
|
||||
{
|
||||
logger(LOG_ERR, "%s: %d: abnormal return value",
|
||||
__FILE__, __LINE__);
|
||||
return ERR;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
55
src/bin/network/atftpd/tftp_def.h
Normal file
55
src/bin/network/atftpd/tftp_def.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* tftp_def.h
|
||||
*
|
||||
* $Id: tftp_def.h,v 1.17 2004/02/13 03:16:09 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef tftp_def_h
|
||||
#define tftp_def_h
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/times.h>
|
||||
#include <netdb.h>
|
||||
|
||||
/* standard return value */
|
||||
#define OK 0
|
||||
#define ERR -1
|
||||
#define ABORT -2
|
||||
#define QUIT -10
|
||||
|
||||
#define MAXLEN 256 /* For file names and such */
|
||||
#define IPADDRLEN 24 /* For IPv4 and IPv6 address string */
|
||||
#define TIMEOUT 5 /* Client timeout */
|
||||
#define S_TIMEOUT 5 /* Server timout. */
|
||||
#define NB_OF_RETRY 5
|
||||
|
||||
/* definition to use tftp_options structure */
|
||||
#define OPT_FILENAME 0
|
||||
#define OPT_MODE 1
|
||||
#define OPT_TSIZE 2
|
||||
#define OPT_TIMEOUT 3
|
||||
#define OPT_BLKSIZE 4
|
||||
#define OPT_MULTICAST 5
|
||||
#define OPT_NUMBER 7
|
||||
|
||||
#define OPT_SIZE 12
|
||||
#define VAL_SIZE MAXLEN
|
||||
|
||||
extern char *tftp_errmsg[9];
|
||||
|
||||
int timeval_diff(struct timeval *res, struct timeval *t1, struct timeval *t0);
|
||||
int print_eng(double value, char *string, int size, char *format);
|
||||
inline char *Strncpy(char *to, const char *from, size_t size);
|
||||
int Gethostbyname(char *addr, struct hostent *host);
|
||||
|
||||
#endif
|
492
src/bin/network/atftpd/tftp_io.c
Normal file
492
src/bin/network/atftpd/tftp_io.c
Normal file
@ -0,0 +1,492 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* tftp_io.c
|
||||
* I/O operation routines common to both client and server
|
||||
*
|
||||
* $Id: tftp_io.c,v 1.24 2004/02/19 01:30:00 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <arpa/tftp.h>
|
||||
#include <errno.h>
|
||||
#include "string.h"
|
||||
#include "tftp_io.h"
|
||||
#include "logger.h"
|
||||
|
||||
/*
|
||||
* 2 bytes string 1 byte string 1 byte string 1 byte string
|
||||
* --------------------------------------------------------------------->
|
||||
*| Opcode | Filename | 0 | Mode | 0 | Opt1 | 0 | Value1 <
|
||||
* --------------------------------------------------------------------->
|
||||
*
|
||||
* string 1 byte string 1 byte
|
||||
* >--------------------------------
|
||||
* < OptN | 0 | ValueN | 0 |
|
||||
* >--------------------------------
|
||||
*/
|
||||
int tftp_send_request(int socket, struct sockaddr_in *sa, short type,
|
||||
char *data_buffer, int data_buffer_size,
|
||||
struct tftp_opt *tftp_options)
|
||||
{
|
||||
int i;
|
||||
int result;
|
||||
int buf_index = 0;
|
||||
struct tftphdr *tftphdr = (struct tftphdr *)data_buffer;
|
||||
char *filename = tftp_options[OPT_FILENAME].value;
|
||||
char *mode = tftp_options[OPT_MODE].value;
|
||||
|
||||
/* write the opcode */
|
||||
tftphdr->th_opcode = htons(type);
|
||||
buf_index += 2;
|
||||
/* write file name */
|
||||
Strncpy(data_buffer + buf_index, filename, data_buffer_size - buf_index);
|
||||
buf_index += strlen(filename);
|
||||
buf_index++;
|
||||
Strncpy(data_buffer + buf_index, mode, data_buffer_size - buf_index);
|
||||
buf_index += strlen(mode);
|
||||
buf_index++;
|
||||
|
||||
for (i = 2; ; i++)
|
||||
{
|
||||
if (strlen(tftp_options[i].option) == 0)
|
||||
break;
|
||||
if (tftp_options[i].enabled && tftp_options[i].specified)
|
||||
{
|
||||
Strncpy(data_buffer + buf_index, tftp_options[i].option,
|
||||
data_buffer_size - buf_index);
|
||||
buf_index += strlen(tftp_options[i].option);
|
||||
buf_index++;
|
||||
Strncpy(data_buffer + buf_index, tftp_options[i].value,
|
||||
data_buffer_size - buf_index);
|
||||
buf_index += strlen(tftp_options[i].value);
|
||||
buf_index++;
|
||||
}
|
||||
}
|
||||
/* send the buffer */
|
||||
result = sendto(socket, data_buffer, buf_index, 0,
|
||||
(struct sockaddr *)sa, sizeof(*sa));
|
||||
if (result < 0)
|
||||
return ERR;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2 bytes 2 bytes
|
||||
* -------------------
|
||||
*| Opcode | Block # |
|
||||
* -------------------
|
||||
*/
|
||||
int tftp_send_ack(int socket, struct sockaddr_in *sa, short block_number)
|
||||
{
|
||||
struct tftphdr tftphdr;
|
||||
int result;
|
||||
|
||||
tftphdr.th_opcode = htons(ACK);
|
||||
tftphdr.th_block = htons(block_number);
|
||||
|
||||
result = sendto(socket, &tftphdr, 4, 0, (struct sockaddr *)sa,
|
||||
sizeof(*sa));
|
||||
if (result < 0)
|
||||
return ERR;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2 bytes string 1 byte string 1 byte string 1 byte string 1 byte
|
||||
* ---------------------------------------------------------------------------
|
||||
*| Opcode | Opt1 | 0 | Value1 | 0 | OptN | 0 | ValueN | 0 |
|
||||
* ---------------------------------------------------------------------------
|
||||
*/
|
||||
int tftp_send_oack(int socket, struct sockaddr_in *sa, struct tftp_opt *tftp_options,
|
||||
char *buffer, int buffer_size)
|
||||
{
|
||||
|
||||
int i;
|
||||
int result;
|
||||
int index = 0;
|
||||
struct tftphdr *tftphdr = (struct tftphdr *)buffer;
|
||||
|
||||
/* write the opcode */
|
||||
tftphdr->th_opcode = htons(OACK);
|
||||
index += 2;
|
||||
|
||||
for (i = 2; i < OPT_NUMBER; i++)
|
||||
{
|
||||
if (tftp_options[i].enabled && tftp_options[i].specified)
|
||||
{
|
||||
Strncpy(buffer + index, tftp_options[i].option, buffer_size - index);
|
||||
index += strlen(tftp_options[i].option);
|
||||
index++;
|
||||
Strncpy(buffer + index, tftp_options[i].value, buffer_size - index);
|
||||
index += strlen(tftp_options[i].value);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
/* send the buffer */
|
||||
result = sendto(socket, buffer, index, 0, (struct sockaddr *)sa,
|
||||
sizeof(*sa));
|
||||
if (result < 0)
|
||||
return ERR;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2 bytes 2 bytes string 1 byte
|
||||
* ---------------------------------------
|
||||
*| Opcode | ErrorCode | ErrMsg | 0 |
|
||||
* ---------------------------------------
|
||||
*/
|
||||
int tftp_send_error(int socket, struct sockaddr_in *sa, short err_code,
|
||||
char *buffer, int buffer_size)
|
||||
{
|
||||
int size;
|
||||
int result;
|
||||
struct tftphdr *tftphdr = (struct tftphdr *)buffer;
|
||||
|
||||
if (err_code > EOPTNEG)
|
||||
return ERR;
|
||||
tftphdr->th_opcode = htons(ERROR);
|
||||
tftphdr->th_code = htons(err_code);
|
||||
Strncpy(tftphdr->th_msg, tftp_errmsg[err_code], buffer_size - 4);
|
||||
|
||||
size = 4 + strlen(tftp_errmsg[err_code]) + 1;
|
||||
|
||||
result = sendto(socket, tftphdr, size, 0, (struct sockaddr *)sa,
|
||||
sizeof(*sa));
|
||||
if (result < 0)
|
||||
return ERR;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 2 bytes 2 bytes N bytes
|
||||
* ----------------------------
|
||||
*| Opcode | Block # | Data |
|
||||
* ----------------------------
|
||||
*/
|
||||
int tftp_send_data(int socket, struct sockaddr_in *sa, short block_number,
|
||||
int size, char *data)
|
||||
{
|
||||
struct tftphdr *tftphdr = (struct tftphdr *)data;
|
||||
int result;
|
||||
|
||||
tftphdr->th_opcode = htons(DATA);
|
||||
tftphdr->th_block = htons(block_number);
|
||||
|
||||
result = sendto(socket, data, size, 0, (struct sockaddr *)sa,
|
||||
sizeof(*sa));
|
||||
if (result < 0)
|
||||
return ERR;
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for a packet. This function can listen on 2 sockets. This is
|
||||
* needed by the multicast tftp client.
|
||||
*/
|
||||
int tftp_get_packet(int sock1, int sock2, int *sock, struct sockaddr_in *sa,
|
||||
struct sockaddr_in *sa_from, struct sockaddr_in *sa_to,
|
||||
int timeout, int *size, char *data)
|
||||
{
|
||||
int result;
|
||||
struct timeval tv;
|
||||
fd_set rfds;
|
||||
struct sockaddr_in from;
|
||||
struct tftphdr *tftphdr = (struct tftphdr *)data;
|
||||
|
||||
struct msghdr msg; /* used to get client's packet info */
|
||||
struct cmsghdr *cmsg;
|
||||
struct iovec iov;
|
||||
char cbuf[1024];
|
||||
|
||||
/* initialise structure */
|
||||
memset(&from, 0, sizeof(from));
|
||||
iov.iov_base = data;
|
||||
iov.iov_len = *size;
|
||||
msg.msg_name = &from;
|
||||
msg.msg_namelen = sizeof(from);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = cbuf;
|
||||
msg.msg_controllen = sizeof(cbuf);
|
||||
|
||||
/* Wait up to five seconds. */
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
/* Watch socket to see when it has input. */
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(sock1, &rfds);
|
||||
if (sock2 > -1)
|
||||
FD_SET(sock2, &rfds);
|
||||
|
||||
/* wait for data on sockets */
|
||||
result = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case -1:
|
||||
logger(LOG_ERR, "select: %s", strerror(errno));
|
||||
return ERR;
|
||||
case 0:
|
||||
return GET_TIMEOUT;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
result = 0;
|
||||
|
||||
if (FD_ISSET(sock1, &rfds))
|
||||
{
|
||||
result = recvmsg(sock1, &msg, 0);
|
||||
if (sock)
|
||||
*sock = sock1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((sock2 > -1) && (FD_ISSET(sock2, &rfds)))
|
||||
{
|
||||
result = recvmsg(sock2, &msg, 0);
|
||||
if (sock)
|
||||
*sock = sock2;
|
||||
}
|
||||
}
|
||||
if (result == 0)
|
||||
return ERR;
|
||||
if (result == -1)
|
||||
{
|
||||
logger(LOG_ERR, "recvmsg: %s", strerror(errno));
|
||||
return ERR;
|
||||
}
|
||||
|
||||
/* if needed read data from message control */
|
||||
if (sa_to)
|
||||
{
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg != NULL && cmsg->cmsg_len >= sizeof(*cmsg);
|
||||
cmsg = CMSG_NXTHDR(&msg, cmsg))
|
||||
{
|
||||
#ifndef __HAIKU__
|
||||
if (cmsg->cmsg_level == SOL_IP
|
||||
&& cmsg->cmsg_type == IP_PKTINFO)
|
||||
{
|
||||
struct in_pktinfo *pktinfo;
|
||||
pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
|
||||
sa_to->sin_addr = pktinfo->ipi_addr;
|
||||
}
|
||||
#else // !__HAIKU__
|
||||
if (cmsg->cmsg_level == IPPROTO_IP
|
||||
&& cmsg->cmsg_type == IP_RECVDSTADDR)
|
||||
{
|
||||
struct in_addr *addr = (struct in_addr *)CMSG_DATA(cmsg);
|
||||
sa_to->sin_addr = *addr;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* return the size to the caller */
|
||||
*size = result;
|
||||
|
||||
/* return the peer address/port to the caller */
|
||||
if (sa_from != NULL)
|
||||
memcpy(sa_from, &from, sizeof(from));
|
||||
|
||||
/* if sa as never been initialised, sa->sin_port is still 0 */
|
||||
if (sa->sin_port == htons(0))
|
||||
memcpy(sa, &from, sizeof(from));
|
||||
|
||||
|
||||
switch (ntohs(tftphdr->th_opcode))
|
||||
{
|
||||
case RRQ:
|
||||
return GET_RRQ;
|
||||
case WRQ:
|
||||
return GET_WRQ;
|
||||
case ACK:
|
||||
return GET_ACK;
|
||||
case OACK:
|
||||
return GET_OACK;
|
||||
case ERROR:
|
||||
return GET_ERROR;
|
||||
case DATA:
|
||||
return GET_DATA;
|
||||
default:
|
||||
return GET_DISCARD;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return ERR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read from file and do netascii conversion if needed
|
||||
*/
|
||||
int tftp_file_read(FILE *fp, char *data_buffer, int data_buffer_size, int block_number,
|
||||
int convert, int *prev_block_number, int *prev_file_pos, int *temp)
|
||||
{
|
||||
int i;
|
||||
int c;
|
||||
char prevchar = *temp & 0xff;
|
||||
char newline = (*temp & 0xff00) >> 8;
|
||||
int data_size;
|
||||
|
||||
if (!convert)
|
||||
{
|
||||
/* In this case, just read the requested data block.
|
||||
Anyway, in the multicast case it can be in random
|
||||
order. */
|
||||
fseek(fp, block_number * data_buffer_size, SEEK_SET);
|
||||
data_size = fread(data_buffer, 1, data_buffer_size, fp);
|
||||
return data_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* When converting data, it become impossible to seek in
|
||||
* the file based on the block number. So we must always
|
||||
* remeber the position in the file from were to read the
|
||||
* data requested by the client. Client can only request data
|
||||
* for the same block or the next, but we cannot assume this
|
||||
* block number will increase at every ACK since it can be
|
||||
* lost in transmission.
|
||||
*
|
||||
* The stategy is to remeber the file position as well as
|
||||
* the block number from the current call to this function.
|
||||
* If the client request a block number different from that
|
||||
* we return ERR.
|
||||
*
|
||||
* If the client request many time the same block, the
|
||||
* netascii conversion is done each time. Since this is not
|
||||
* a normal condition it should not be a problem for system
|
||||
* performance.
|
||||
*
|
||||
*/
|
||||
if ((block_number != *prev_block_number) && (block_number != *prev_block_number + 1))
|
||||
return ERR;
|
||||
if (block_number == *prev_block_number)
|
||||
fseek(fp, *prev_file_pos, SEEK_SET);
|
||||
|
||||
*prev_block_number = block_number;
|
||||
*prev_file_pos = ftell(fp);
|
||||
|
||||
/*
|
||||
* convert to netascii, based on netkit-tftp-0.17 routine in tftpsubs.c
|
||||
* i index output buffer
|
||||
*/
|
||||
for (i = 0; i < data_buffer_size; i++)
|
||||
{
|
||||
if (newline)
|
||||
{
|
||||
if (prevchar == '\n')
|
||||
c = '\n'; /* lf to cr,lf */
|
||||
else
|
||||
c = '\0'; /* cr to cr,nul */
|
||||
newline = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = fgetc(fp);
|
||||
if (c == EOF)
|
||||
break;
|
||||
if (c == '\n' || c == '\r')
|
||||
{
|
||||
prevchar = c;
|
||||
c = '\r';
|
||||
newline = 1;
|
||||
}
|
||||
}
|
||||
data_buffer[i] = c;
|
||||
}
|
||||
/* save state */
|
||||
*temp = (newline << 8) | prevchar;
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write to file and do netascii conversion if needed
|
||||
*/
|
||||
int tftp_file_write(FILE *fp, char *data_buffer, int data_buffer_size, int block_number, int data_size,
|
||||
int convert, int *prev_block_number, int *temp)
|
||||
{
|
||||
int i;
|
||||
int c;
|
||||
char prevchar = *temp;
|
||||
|
||||
if (!convert)
|
||||
{
|
||||
/* Simple case, just seek and write */
|
||||
fseek(fp, (block_number - 1) * data_buffer_size, SEEK_SET);
|
||||
data_size = fwrite(data_buffer, 1, data_size, fp);
|
||||
return data_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Same principle than for reading, but simpler since when client
|
||||
* send same block twice there is no need to rewrite it to the
|
||||
* file
|
||||
*/
|
||||
if ((block_number != *prev_block_number) && (block_number != *prev_block_number + 1))
|
||||
return ERR;
|
||||
if (block_number == *prev_block_number)
|
||||
return data_size;
|
||||
|
||||
*prev_block_number = block_number;
|
||||
|
||||
/*
|
||||
* convert to netascii, based on netkit-tftp-0.17 routine in tftpsubs.c
|
||||
* i index input buffer
|
||||
*/
|
||||
for (i = 0; i < data_size; i++)
|
||||
{
|
||||
c = data_buffer[i];
|
||||
if (prevchar == '\r')
|
||||
{
|
||||
if (c == '\n')
|
||||
{
|
||||
fseek(fp, -1, SEEK_CUR); /* cr,lf to lf */
|
||||
if (fputc(c, fp) == EOF)
|
||||
break;
|
||||
}
|
||||
else if (c != '\0') /* cr,nul to cr */
|
||||
{
|
||||
if (fputc(c, fp) == EOF)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fputc(c, fp) == EOF)
|
||||
break;
|
||||
}
|
||||
prevchar = c;
|
||||
}
|
||||
|
||||
/* save state */
|
||||
*temp = prevchar;
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
59
src/bin/network/atftpd/tftp_io.h
Normal file
59
src/bin/network/atftpd/tftp_io.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* tftp_io.h
|
||||
*
|
||||
* $Id: tftp_io.h,v 1.18 2004/02/13 03:16:09 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef tftp_io_h
|
||||
#define tftp_io_h
|
||||
|
||||
#include <arpa/tftp.h>
|
||||
#include <sys/socket.h>
|
||||
#include "tftp_def.h"
|
||||
#include "options.h"
|
||||
|
||||
/* missing from <arpa/tftp.h> */
|
||||
/* new opcode */
|
||||
#define OACK 06
|
||||
/* new error code */
|
||||
#define EOPTNEG 8 /* error in option negociation */
|
||||
|
||||
/* return value of tftp_get_packet */
|
||||
#define GET_DISCARD 0
|
||||
#define GET_TIMEOUT 1
|
||||
#define GET_RRQ 2
|
||||
#define GET_WRQ 3
|
||||
#define GET_ACK 4
|
||||
#define GET_OACK 5
|
||||
#define GET_ERROR 6
|
||||
#define GET_DATA 7
|
||||
|
||||
/* functions prototype */
|
||||
int tftp_send_request(int socket, struct sockaddr_in *s_inn, short type,
|
||||
char *data_buffer, int data_buffer_size,
|
||||
struct tftp_opt *tftp_options);
|
||||
int tftp_send_ack(int socket, struct sockaddr_in *s_inn, short block_number);
|
||||
int tftp_send_oack(int socket, struct sockaddr_in *s_inn, struct tftp_opt *tftp_options,
|
||||
char *buffer, int buffer_size);
|
||||
int tftp_send_error(int socket, struct sockaddr_in *s_inn, short err_code,
|
||||
char *buffer, int buffer_size);
|
||||
int tftp_send_data(int socket, struct sockaddr_in *s_inn, short block_number,
|
||||
int size, char *data);
|
||||
int tftp_get_packet(int sock1, int sock2, int *sock, struct sockaddr_in *sa,
|
||||
struct sockaddr_in *from, struct sockaddr_in *to,
|
||||
int timeout, int *size, char *data);
|
||||
int tftp_file_read(FILE *fp, char *buffer, int buffer_size, int block_number, int convert,
|
||||
int *prev_block_number, int *prev_file_pos, int *temp);
|
||||
int tftp_file_write(FILE *fp, char *data_buffer, int data_buffer_size, int block_number,
|
||||
int data_size, int convert, int *prev_block_number, int *temp);
|
||||
#endif
|
1149
src/bin/network/atftpd/tftpd.c
Normal file
1149
src/bin/network/atftpd/tftpd.c
Normal file
File diff suppressed because it is too large
Load Diff
114
src/bin/network/atftpd/tftpd.h
Normal file
114
src/bin/network/atftpd/tftpd.h
Normal file
@ -0,0 +1,114 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* tftpd.h
|
||||
*
|
||||
* $Id: tftpd.h,v 1.22 2004/02/27 02:05:26 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef tftpd_h
|
||||
#define tftpd_h
|
||||
|
||||
#include <pthread.h>
|
||||
#include <arpa/tftp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "tftp_io.h"
|
||||
|
||||
/*
|
||||
* Per thread data. There is a thread for each client or group
|
||||
* (multicast) of client.
|
||||
*/
|
||||
struct thread_data {
|
||||
pthread_t tid;
|
||||
|
||||
/* private to thread */
|
||||
char *data_buffer;
|
||||
int data_buffer_size;
|
||||
|
||||
int timeout;
|
||||
int checkport; /* Disable TID check. Violate RFC */
|
||||
int mcast_switch_client; /* When set, server will switch to next client
|
||||
on first timeout from the current client. */
|
||||
int trace;
|
||||
|
||||
int sockfd;
|
||||
|
||||
/* multicast stuff */
|
||||
short mc_port; /* multicast port */
|
||||
char *mc_addr; /* multicast address */
|
||||
struct sockaddr_in sa_mcast;
|
||||
struct ip_mreq mcastaddr;
|
||||
u_char mcast_ttl;
|
||||
|
||||
/*
|
||||
* Self can read/write until client_ready is set. Then only allowed to read.
|
||||
* Other thread can read it only when client_ready is set. Remember that access
|
||||
* to client_ready bellow is protected by a mutex.
|
||||
*/
|
||||
struct tftp_opt *tftp_options;
|
||||
|
||||
/*
|
||||
* Must lock to insert in the list or search, but not to read or write
|
||||
* in the client_info structure, since only the propriotary thread do it.
|
||||
*/
|
||||
pthread_mutex_t client_mutex;
|
||||
struct client_info *client_info;
|
||||
int client_ready; /* one if other thread may add client */
|
||||
|
||||
/* must be lock (list lock) to update */
|
||||
struct thread_data *prev;
|
||||
struct thread_data *next;
|
||||
};
|
||||
|
||||
struct client_info {
|
||||
struct sockaddr_in client;
|
||||
int done; /* that client as receive it's file */
|
||||
struct client_info *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* Functions defined in tftpd_file.c
|
||||
*/
|
||||
int tftpd_rules_check(char *filename);
|
||||
int tftpd_receive_file(struct thread_data *data);
|
||||
int tftpd_send_file(struct thread_data *data);
|
||||
|
||||
/*
|
||||
* Defined in tftpd_list.c, operation on thread_data list.
|
||||
*/
|
||||
int tftpd_list_add(struct thread_data *new);
|
||||
int tftpd_list_remove(struct thread_data *old);
|
||||
int tftpd_list_num_of_thread(void);
|
||||
int tftpd_list_find_multicast_server_and_add(struct thread_data **thread,
|
||||
struct thread_data *data,
|
||||
struct client_info *client);
|
||||
/*
|
||||
* Defined in tftpd_list.c, operation on client structure list.
|
||||
*/
|
||||
inline void tftpd_clientlist_ready(struct thread_data *thread);
|
||||
void tftpd_clientlist_remove(struct thread_data *thread,
|
||||
struct client_info *client);
|
||||
void tftpd_clientlist_free(struct thread_data *thread);
|
||||
int tftpd_clientlist_done(struct thread_data *thread,
|
||||
struct client_info *client,
|
||||
struct sockaddr_in *sock);
|
||||
int tftpd_clientlist_next(struct thread_data *thread,
|
||||
struct client_info **client);
|
||||
void tftpd_list_kill_threads(void);
|
||||
|
||||
/*
|
||||
* Defines in tftpd_mcast.c
|
||||
*/
|
||||
int tftpd_mcast_get_tid(char **addr, short *port);
|
||||
int tftpd_mcast_free_tid(char *addr, short port);
|
||||
int tftpd_mcast_parse_opt(char *addr, char *ports);
|
||||
|
||||
#endif
|
980
src/bin/network/atftpd/tftpd_file.c
Normal file
980
src/bin/network/atftpd/tftpd_file.c
Normal file
@ -0,0 +1,980 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* tftpd_file.c
|
||||
* state machine for file transfer on the server side
|
||||
*
|
||||
* $Id: tftpd_file.c,v 1.51 2004/02/18 02:21:47 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "tftpd.h"
|
||||
#include "tftp_io.h"
|
||||
#include "tftp_def.h"
|
||||
#include "logger.h"
|
||||
#include "options.h"
|
||||
#ifdef HAVE_PCRE
|
||||
#include "tftpd_pcre.h"
|
||||
#endif
|
||||
|
||||
#define S_BEGIN 0
|
||||
#define S_SEND_REQ 1
|
||||
#define S_SEND_ACK 2
|
||||
#define S_SEND_OACK 3
|
||||
#define S_SEND_DATA 4
|
||||
#define S_WAIT_PACKET 5
|
||||
#define S_REQ_RECEIVED 6
|
||||
#define S_ACK_RECEIVED 7
|
||||
#define S_OACK_RECEIVED 8
|
||||
#define S_DATA_RECEIVED 9
|
||||
#define S_ABORT 10
|
||||
#define S_END 11
|
||||
|
||||
|
||||
/* read only variables unless for the main thread, at initialisation */
|
||||
extern char directory[MAXLEN];
|
||||
/* read only except for the main thread */
|
||||
extern int tftpd_cancel;
|
||||
|
||||
#ifdef HAVE_PCRE
|
||||
extern tftpd_pcre_self_t *pcre_top;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Rules for filenames. This is common to both tftpd_recieve_file
|
||||
* and tftpd_send_file It is placed here to make sure the rules are
|
||||
* consistent and clearer.
|
||||
*/
|
||||
int tftpd_rules_check(char *filename)
|
||||
{
|
||||
char string[MAXLEN];
|
||||
|
||||
/* If the filename does not start with the directory, change it */
|
||||
if (strncmp(directory, filename, strlen(directory)) != 0)
|
||||
{
|
||||
/* Append the prefix directory to the requested file. */
|
||||
snprintf(string, sizeof(string), "%s%s",
|
||||
directory, filename);
|
||||
Strncpy(filename, string, MAXLEN);
|
||||
}
|
||||
|
||||
/* If the filename contain /../ sequences, we forbid the access */
|
||||
if (strstr(filename, "/../") != NULL)
|
||||
{
|
||||
/* Illegal access. */
|
||||
logger(LOG_INFO, "File name with /../ are forbidden");
|
||||
return ERR;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive a file. It is implemented as a state machine using a while loop
|
||||
* and a switch statement. Function flow is as follow:
|
||||
* - sanity check
|
||||
* - check client's request
|
||||
* - enter state machine
|
||||
*
|
||||
* 1) send a ACK or OACK
|
||||
* 2) wait replay
|
||||
* - if DATA packet, read it, send an acknoledge, goto 2
|
||||
* - if ERROR abort
|
||||
* - if TIMEOUT goto previous state
|
||||
*/
|
||||
int tftpd_receive_file(struct thread_data *data)
|
||||
{
|
||||
int state = S_BEGIN;
|
||||
int timeout_state = state;
|
||||
int result;
|
||||
int block_number = 0;
|
||||
int data_size;
|
||||
int sockfd = data->sockfd;
|
||||
struct sockaddr_in *sa = &data->client_info->client;
|
||||
struct sockaddr_in from;
|
||||
struct tftphdr *tftphdr = (struct tftphdr *)data->data_buffer;
|
||||
FILE *fp;
|
||||
char filename[MAXLEN];
|
||||
char string[MAXLEN];
|
||||
int timeout = data->timeout;
|
||||
int number_of_timeout = 0;
|
||||
int all_blocks_received = 0; /* temporary kludge */
|
||||
int convert = 0; /* if true, do netascii convertion */
|
||||
|
||||
int prev_block_number = 0; /* needed to support netascii convertion */
|
||||
int temp = 0;
|
||||
|
||||
/* look for mode option */
|
||||
if (strcasecmp(data->tftp_options[OPT_MODE].value, "netascii") == 0)
|
||||
{
|
||||
convert = 1;
|
||||
logger(LOG_DEBUG, "will do netascii convertion");
|
||||
}
|
||||
|
||||
/* file name verification */
|
||||
Strncpy(filename, data->tftp_options[OPT_FILENAME].value,
|
||||
MAXLEN);
|
||||
if (tftpd_rules_check(filename) != OK)
|
||||
{
|
||||
tftp_send_error(sockfd, sa, EACCESS, data->data_buffer, data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EACCESS,
|
||||
tftp_errmsg[EACCESS]);
|
||||
return ERR;
|
||||
}
|
||||
|
||||
/* Open the file for writing. */
|
||||
if ((fp = fopen(filename, "w")) == NULL)
|
||||
{
|
||||
/* Can't create the file. */
|
||||
logger(LOG_INFO, "Can't open %s for writing", filename);
|
||||
tftp_send_error(sockfd, sa, EACCESS, data->data_buffer, data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EACCESS,
|
||||
tftp_errmsg[EACCESS]);
|
||||
return ERR;
|
||||
}
|
||||
|
||||
/* tsize option */
|
||||
if (((result = opt_get_tsize(data->tftp_options)) > -1) && !convert)
|
||||
{
|
||||
opt_set_tsize(result, data->tftp_options);
|
||||
logger(LOG_DEBUG, "tsize option -> %d", result);
|
||||
}
|
||||
|
||||
/* timeout option */
|
||||
if ((result = opt_get_timeout(data->tftp_options)) > -1)
|
||||
{
|
||||
if ((result < 1) || (result > 255))
|
||||
{
|
||||
tftp_send_error(sockfd, sa, EOPTNEG, data->data_buffer, data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EOPTNEG,
|
||||
tftp_errmsg[EOPTNEG]);
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
}
|
||||
timeout = result;
|
||||
opt_set_timeout(timeout, data->tftp_options);
|
||||
logger(LOG_DEBUG, "timeout option -> %d", timeout);
|
||||
}
|
||||
|
||||
/* blksize options */
|
||||
if ((result = opt_get_blksize(data->tftp_options)) > -1)
|
||||
{
|
||||
if ((result < 8) || (result > 65464))
|
||||
{
|
||||
tftp_send_error(sockfd, sa, EOPTNEG, data->data_buffer, data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EOPTNEG,
|
||||
tftp_errmsg[EOPTNEG]);
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
}
|
||||
|
||||
data->data_buffer_size = result + 4;
|
||||
data->data_buffer = realloc(data->data_buffer, data->data_buffer_size);
|
||||
|
||||
if (data->data_buffer == NULL)
|
||||
{
|
||||
logger(LOG_ERR, "memory allocation failure");
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
}
|
||||
tftphdr = (struct tftphdr *)data->data_buffer;
|
||||
|
||||
if (data->data_buffer == NULL)
|
||||
{
|
||||
tftp_send_error(sockfd, sa, ENOSPACE, data->data_buffer, data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", ENOSPACE,
|
||||
tftp_errmsg[ENOSPACE]);
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
}
|
||||
opt_set_blksize(result, data->tftp_options);
|
||||
logger(LOG_DEBUG, "blksize option -> %d", result);
|
||||
}
|
||||
|
||||
/* that's it, we start receiving the file */
|
||||
while (1)
|
||||
{
|
||||
if (tftpd_cancel)
|
||||
{
|
||||
logger(LOG_DEBUG, "thread cancelled");
|
||||
tftp_send_error(sockfd, sa, EUNDEF, data->data_buffer, data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EUNDEF,
|
||||
tftp_errmsg[EUNDEF]);
|
||||
state = S_ABORT;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case S_BEGIN:
|
||||
/* Did the client request RFC1350 options ?*/
|
||||
if (opt_support_options(data->tftp_options))
|
||||
state = S_SEND_OACK;
|
||||
else
|
||||
state = S_SEND_ACK;
|
||||
break;
|
||||
case S_SEND_ACK:
|
||||
timeout_state = state;
|
||||
tftp_send_ack(sockfd, sa, block_number);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ACK <block: %d>", block_number);
|
||||
if (all_blocks_received)
|
||||
state = S_END;
|
||||
else
|
||||
state = S_WAIT_PACKET;
|
||||
break;
|
||||
case S_SEND_OACK:
|
||||
timeout_state = state;
|
||||
tftp_send_oack(sockfd, sa, data->tftp_options,
|
||||
data->data_buffer, data->data_buffer_size);
|
||||
opt_options_to_string(data->tftp_options, string, MAXLEN);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent OACK <%s>", string);
|
||||
state = S_WAIT_PACKET;
|
||||
break;
|
||||
case S_WAIT_PACKET:
|
||||
data_size = data->data_buffer_size;
|
||||
result = tftp_get_packet(sockfd, -1, NULL, sa, &from, NULL,
|
||||
timeout, &data_size, data->data_buffer);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case GET_TIMEOUT:
|
||||
number_of_timeout++;
|
||||
if (number_of_timeout > NB_OF_RETRY)
|
||||
{
|
||||
logger(LOG_INFO, "client (%s) not responding",
|
||||
inet_ntoa(data->client_info->client.sin_addr));
|
||||
state = S_END;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger(LOG_WARNING, "timeout: retrying...");
|
||||
state = timeout_state;
|
||||
}
|
||||
break;
|
||||
case GET_ERROR:
|
||||
/*
|
||||
* This does not work correctly if load balancers are
|
||||
* employed on one side of a multihomed host. ie, the
|
||||
* tftp RRQ goes through the load balancer and has the
|
||||
* source ports changed while the multicast traffic
|
||||
* bypasses the load balancers.
|
||||
*
|
||||
* **** It is not compliant with RFC1350 to skip this
|
||||
* **** test since the port number is the TID. Use this
|
||||
* **** only if you know what you're doing.
|
||||
*/
|
||||
if (sa->sin_port != from.sin_port)
|
||||
{
|
||||
if (data->checkport)
|
||||
{
|
||||
logger(LOG_WARNING, "packet discarded <%s>",
|
||||
inet_ntoa(from.sin_addr));
|
||||
break;
|
||||
}
|
||||
else
|
||||
logger(LOG_WARNING, "source port mismatch, check bypassed");
|
||||
}
|
||||
Strncpy(string, tftphdr->th_msg,
|
||||
(((data_size - 4) > MAXLEN) ? MAXLEN :
|
||||
(data_size - 4)));
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "received ERROR <code: %d, msg: %s>",
|
||||
ntohs(tftphdr->th_code), string);
|
||||
state = S_ABORT;
|
||||
break;
|
||||
case GET_DATA:
|
||||
/* Check that source port match */
|
||||
if (sa->sin_port != from.sin_port)
|
||||
{
|
||||
if (data->checkport)
|
||||
{
|
||||
logger(LOG_WARNING, "packet discarded <%s>",
|
||||
inet_ntoa(from.sin_addr));
|
||||
break;
|
||||
}
|
||||
else
|
||||
logger(LOG_WARNING, "source port mismatch, check bypassed");
|
||||
}
|
||||
number_of_timeout = 0;
|
||||
state = S_DATA_RECEIVED;
|
||||
break;
|
||||
case GET_DISCARD:
|
||||
/* FIXME: should we increment number_of_timeout */
|
||||
logger(LOG_WARNING, "packet discarded <%s>",
|
||||
inet_ntoa(from.sin_addr));
|
||||
break;
|
||||
case ERR:
|
||||
logger(LOG_ERR, "%s: %d: recvfrom: %s",
|
||||
__FILE__, __LINE__, strerror(errno));
|
||||
state = S_ABORT;
|
||||
break;
|
||||
default:
|
||||
logger(LOG_ERR, "%s: %d: abnormal return value %d",
|
||||
__FILE__, __LINE__, result);
|
||||
state = S_ABORT;
|
||||
}
|
||||
break;
|
||||
case S_DATA_RECEIVED:
|
||||
/* We need to seek to the right place in the file */
|
||||
block_number = ntohs(tftphdr->th_block);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "received DATA <block: %d, size: %d>",
|
||||
block_number, data_size - 4);
|
||||
|
||||
if (tftp_file_write(fp, tftphdr->th_data, data->data_buffer_size - 4, block_number,
|
||||
data_size - 4, convert, &prev_block_number, &temp)
|
||||
!= data_size - 4)
|
||||
{
|
||||
logger(LOG_ERR, "%s: %d: error writing to file %s",
|
||||
__FILE__, __LINE__, filename);
|
||||
tftp_send_error(sockfd, sa, ENOSPACE, data->data_buffer,
|
||||
data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>",
|
||||
ENOSPACE, tftp_errmsg[ENOSPACE]);
|
||||
state = S_ABORT;
|
||||
break;
|
||||
}
|
||||
if (data_size < data->data_buffer_size)
|
||||
all_blocks_received = 1;
|
||||
else
|
||||
all_blocks_received = 0;
|
||||
state = S_SEND_ACK;
|
||||
break;
|
||||
case S_END:
|
||||
fclose(fp);
|
||||
return OK;
|
||||
case S_ABORT:
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
default:
|
||||
fclose(fp);
|
||||
logger(LOG_ERR, "%s: %d: tftpd_file.c: huh?",
|
||||
__FILE__, __LINE__);
|
||||
return ERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a file. It is implemented as a state machine using a while loop
|
||||
* and a switch statement. Function flow is as follow:
|
||||
* - sanity check
|
||||
* - check client's request
|
||||
* - enter state machine
|
||||
*
|
||||
* 1) send a DATA or OACK
|
||||
* 2) wait replay
|
||||
* - if ACK, goto 3
|
||||
* - if ERROR abort
|
||||
* - if TIMEOUT goto previous state
|
||||
* 3) send data, goto 2
|
||||
*/
|
||||
int tftpd_send_file(struct thread_data *data)
|
||||
{
|
||||
int state = S_BEGIN;
|
||||
int timeout_state = state;
|
||||
int result;
|
||||
int block_number = 0;
|
||||
int last_block = -1;
|
||||
int data_size;
|
||||
struct sockaddr_in *sa = &data->client_info->client;
|
||||
struct sockaddr_in from;
|
||||
int sockfd = data->sockfd;
|
||||
struct tftphdr *tftphdr = (struct tftphdr *)data->data_buffer;
|
||||
FILE *fp;
|
||||
char filename[MAXLEN];
|
||||
char string[MAXLEN];
|
||||
int timeout = data->timeout;
|
||||
int number_of_timeout = 0;
|
||||
int mcast_switch = data->mcast_switch_client;
|
||||
struct stat file_stat;
|
||||
int convert = 0; /* if true, do netascii conversion */
|
||||
struct thread_data *thread = NULL; /* used when looking for a multicast
|
||||
thread */
|
||||
int multicast = 0; /* set to 1 if multicast */
|
||||
|
||||
struct client_info *client_info = data->client_info;
|
||||
struct client_info *client_old = NULL;
|
||||
struct tftp_opt options[OPT_NUMBER];
|
||||
|
||||
int prev_block_number = 0; /* needed to support netascii convertion */
|
||||
int prev_file_pos = 0;
|
||||
int temp = 0;
|
||||
|
||||
/* look for mode option */
|
||||
if (strcasecmp(data->tftp_options[OPT_MODE].value, "netascii") == 0)
|
||||
{
|
||||
convert = 1;
|
||||
logger(LOG_DEBUG, "will do netascii convertion");
|
||||
}
|
||||
|
||||
/* file name verification */
|
||||
Strncpy(filename, data->tftp_options[OPT_FILENAME].value,
|
||||
MAXLEN);
|
||||
if (tftpd_rules_check(filename) != OK)
|
||||
{
|
||||
tftp_send_error(sockfd, sa, EACCESS, data->data_buffer, data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EACCESS,
|
||||
tftp_errmsg[EACCESS]);
|
||||
return ERR;
|
||||
}
|
||||
|
||||
/* verify that the requested file exist */
|
||||
fp = fopen(filename, "r");
|
||||
|
||||
#ifdef HAVE_PCRE
|
||||
if (fp == NULL)
|
||||
{
|
||||
/* Verify if this file have a working subsitution */
|
||||
if (pcre_top != NULL)
|
||||
{
|
||||
if (tftpd_pcre_sub(pcre_top, string, MAXLEN,
|
||||
data->tftp_options[OPT_FILENAME].value) < 0)
|
||||
{
|
||||
logger(LOG_DEBUG, "PCRE failed to match");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger(LOG_INFO, "PCRE mapped %s -> %s",
|
||||
data->tftp_options[OPT_FILENAME].value, string);
|
||||
Strncpy(filename, string, MAXLEN);
|
||||
/* recheck those rules */
|
||||
if (tftpd_rules_check(filename) != OK)
|
||||
{
|
||||
tftp_send_error(sockfd, sa, EACCESS, data->data_buffer,
|
||||
data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EACCESS,
|
||||
tftp_errmsg[EACCESS]);
|
||||
return ERR;
|
||||
}
|
||||
/* write back the new file name to the option structure */
|
||||
opt_set_options(data->tftp_options, "filename", filename);
|
||||
/* try to open this new file */
|
||||
fp = fopen(filename, "r");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (fp == NULL)
|
||||
{
|
||||
tftp_send_error(sockfd, sa, ENOTFOUND, data->data_buffer, data->data_buffer_size);
|
||||
logger(LOG_INFO, "File %s not found", filename);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", ENOTFOUND,
|
||||
tftp_errmsg[ENOTFOUND]);
|
||||
return ERR;
|
||||
}
|
||||
|
||||
/* To return the size of the file with tsize argument */
|
||||
fstat(fileno(fp), &file_stat);
|
||||
|
||||
/* tsize option */
|
||||
if ((opt_get_tsize(data->tftp_options) > -1) && !convert)
|
||||
{
|
||||
opt_set_tsize(file_stat.st_size, data->tftp_options);
|
||||
logger(LOG_INFO, "tsize option -> %d", file_stat.st_size);
|
||||
}
|
||||
|
||||
/* timeout option */
|
||||
if ((result = opt_get_timeout(data->tftp_options)) > -1)
|
||||
{
|
||||
if ((result < 1) || (result > 255))
|
||||
{
|
||||
tftp_send_error(sockfd, sa, EOPTNEG, data->data_buffer, data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EOPTNEG,
|
||||
tftp_errmsg[EOPTNEG]);
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
}
|
||||
timeout = result;
|
||||
opt_set_timeout(timeout, data->tftp_options);
|
||||
logger(LOG_INFO, "timeout option -> %d", timeout);
|
||||
}
|
||||
|
||||
/* blksize options */
|
||||
if ((result = opt_get_blksize(data->tftp_options)) > -1)
|
||||
{
|
||||
if ((result < 8) || (result > 65464))
|
||||
{
|
||||
tftp_send_error(sockfd, sa, EOPTNEG, data->data_buffer, data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EOPTNEG,
|
||||
tftp_errmsg[EOPTNEG]);
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
}
|
||||
|
||||
data->data_buffer_size = result + 4;
|
||||
data->data_buffer = realloc(data->data_buffer, data->data_buffer_size);
|
||||
|
||||
if (data->data_buffer == NULL)
|
||||
{
|
||||
logger(LOG_ERR, "memory allocation failure");
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
}
|
||||
tftphdr = (struct tftphdr *)data->data_buffer;
|
||||
|
||||
if (data->data_buffer == NULL)
|
||||
{
|
||||
tftp_send_error(sockfd, sa, ENOSPACE, data->data_buffer, data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", ENOSPACE,
|
||||
tftp_errmsg[ENOSPACE]);
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
}
|
||||
opt_set_blksize(result, data->tftp_options);
|
||||
logger(LOG_INFO, "blksize option -> %d", result);
|
||||
}
|
||||
|
||||
/* Verify that the file can be sent in 2^16 block of BLKSIZE octets */
|
||||
if ((file_stat.st_size / (data->data_buffer_size - 4)) > 65535)
|
||||
{
|
||||
tftp_send_error(sockfd, sa, EUNDEF, data->data_buffer, data->data_buffer_size);
|
||||
logger(LOG_NOTICE, "Requested file to big, increase BLKSIZE");
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EUNDEF,
|
||||
tftp_errmsg[EUNDEF]);
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
}
|
||||
|
||||
/* multicast option */
|
||||
if (data->tftp_options[OPT_MULTICAST].specified &&
|
||||
data->tftp_options[OPT_MULTICAST].enabled && !convert)
|
||||
{
|
||||
/*
|
||||
* Find a server with the same options to give up the client.
|
||||
*/
|
||||
logger(LOG_DEBUG, "Searching a server thread to give up this client");
|
||||
result = tftpd_list_find_multicast_server_and_add(&thread, data, data->client_info);
|
||||
if ( result > 0)
|
||||
{
|
||||
/* add this client to its list of client */
|
||||
if (result == 1)
|
||||
logger(LOG_DEBUG, "Added client %p to thread %p", data->client_info,
|
||||
thread);
|
||||
else
|
||||
logger(LOG_DEBUG, "Client (%p) is already in list of thread %p",
|
||||
data->client_info, thread);
|
||||
|
||||
/* NULL our own pointer so we don't free memory */
|
||||
if (result == 1)
|
||||
data->client_info = NULL;
|
||||
|
||||
/* Look at needed information to oack that client */
|
||||
opt_set_multicast(data->tftp_options, thread->mc_addr,
|
||||
thread->mc_port, 0);
|
||||
logger(LOG_INFO, "multicast option -> %s,%d,%d",
|
||||
thread->mc_addr, thread->mc_port, 0);
|
||||
|
||||
/* Send an OACK to that client. There is a possible race condition
|
||||
here where the new server thread OACK this client before us. This should
|
||||
not be a problem: the client thread will receive a second OACK and fall
|
||||
back to non master mode. Then the server will timeout and either resend
|
||||
OACK or continu with the next client */
|
||||
opt_options_to_string(data->tftp_options, string, MAXLEN);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent OACK <%s>", string);
|
||||
tftp_send_oack(thread->sockfd, sa, data->tftp_options,
|
||||
data->data_buffer, data->data_buffer_size);
|
||||
|
||||
/* We are done */
|
||||
logger(LOG_INFO, "Client transfered to %p", thread);
|
||||
fclose(fp);
|
||||
return OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* configure socket, get an IP address */
|
||||
if (tftpd_mcast_get_tid(&data->mc_addr, &data->mc_port) != OK)
|
||||
{
|
||||
logger(LOG_ERR, "No multicast address/port available");
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
}
|
||||
logger(LOG_DEBUG, "mcast_addr: %s, mcast_port: %d",
|
||||
data->mc_addr, data->mc_port);
|
||||
|
||||
/* convert address */
|
||||
if (inet_aton(data->mc_addr, &data->sa_mcast.sin_addr) == 0)
|
||||
{
|
||||
logger(LOG_ERR, "bad address %s\n",data->mc_addr);
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
}
|
||||
data->sa_mcast.sin_family = AF_INET; /* FIXME: IPv6 */
|
||||
data->sa_mcast.sin_port = htons(data->mc_port);
|
||||
/* verify address is multicast */
|
||||
if (!IN_MULTICAST(ntohl(data->sa_mcast.sin_addr.s_addr)))
|
||||
{
|
||||
logger(LOG_ERR, "bad multicast address %s\n",
|
||||
inet_ntoa(data->sa_mcast.sin_addr));
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
}
|
||||
|
||||
/* initialise multicast address structure */
|
||||
data->mcastaddr.imr_multiaddr.s_addr =
|
||||
data->sa_mcast.sin_addr.s_addr;
|
||||
data->mcastaddr.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||
setsockopt(data->sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
|
||||
&data->mcast_ttl, sizeof(data->mcast_ttl));
|
||||
|
||||
/* set options data for OACK */
|
||||
opt_set_multicast(data->tftp_options, data->mc_addr,
|
||||
data->mc_port, 1);
|
||||
logger(LOG_INFO, "multicast option -> %s,%d,%d", data->mc_addr,
|
||||
data->mc_port, 1);
|
||||
|
||||
/* the socket must be unconnected for multicast */
|
||||
sa->sin_family = AF_UNSPEC;
|
||||
connect(sockfd, (struct sockaddr *)sa, sizeof(sa));
|
||||
|
||||
/* set multicast flag */
|
||||
multicast = 1;
|
||||
/* Now ready to receive new clients */
|
||||
tftpd_clientlist_ready(data);
|
||||
}
|
||||
}
|
||||
|
||||
/* copy options to local structure, used when falling back a client to slave */
|
||||
memcpy(options, data->tftp_options, sizeof(options));
|
||||
opt_set_multicast(options, data->mc_addr, data->mc_port, 0);
|
||||
|
||||
/* That's it, ready to send the file */
|
||||
while (1)
|
||||
{
|
||||
if (tftpd_cancel)
|
||||
{
|
||||
/* Send error to all client */
|
||||
logger(LOG_DEBUG, "thread cancelled");
|
||||
do
|
||||
{
|
||||
tftpd_clientlist_done(data, client_info, NULL);
|
||||
tftp_send_error(sockfd, &client_info->client,
|
||||
EUNDEF, data->data_buffer, data->data_buffer_size);
|
||||
if (data->trace)
|
||||
{
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s> to %s", EUNDEF,
|
||||
tftp_errmsg[EUNDEF], inet_ntoa(client_info->client.sin_addr));
|
||||
}
|
||||
} while (tftpd_clientlist_next(data, &client_info) == 1);
|
||||
state = S_ABORT;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case S_BEGIN:
|
||||
if (opt_support_options(data->tftp_options))
|
||||
state = S_SEND_OACK;
|
||||
else
|
||||
state = S_SEND_DATA;
|
||||
break;
|
||||
case S_SEND_OACK:
|
||||
timeout_state = state;
|
||||
opt_options_to_string(data->tftp_options, string, MAXLEN);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent OACK <%s>", string);
|
||||
tftp_send_oack(sockfd, sa, data->tftp_options,
|
||||
data->data_buffer, data->data_buffer_size);
|
||||
state = S_WAIT_PACKET;
|
||||
break;
|
||||
case S_SEND_DATA:
|
||||
timeout_state = state;
|
||||
|
||||
data_size = tftp_file_read(fp, tftphdr->th_data, data->data_buffer_size - 4, block_number,
|
||||
convert, &prev_block_number, &prev_file_pos, &temp);
|
||||
data_size += 4; /* need to consider tftp header */
|
||||
|
||||
/* record the last block number */
|
||||
if (feof(fp))
|
||||
last_block = block_number;
|
||||
|
||||
if (multicast)
|
||||
{
|
||||
tftp_send_data(sockfd, &data->sa_mcast,
|
||||
block_number + 1, data_size,
|
||||
data->data_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
tftp_send_data(sockfd, sa, block_number + 1,
|
||||
data_size, data->data_buffer);
|
||||
}
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent DATA <block: %d, size %d>",
|
||||
block_number + 1, data_size - 4);
|
||||
state = S_WAIT_PACKET;
|
||||
break;
|
||||
case S_WAIT_PACKET:
|
||||
data_size = data->data_buffer_size;
|
||||
result = tftp_get_packet(sockfd, -1, NULL, sa, &from, NULL,
|
||||
timeout, &data_size, data->data_buffer);
|
||||
switch (result)
|
||||
{
|
||||
case GET_TIMEOUT:
|
||||
number_of_timeout++;
|
||||
|
||||
if (number_of_timeout > NB_OF_RETRY)
|
||||
{
|
||||
logger(LOG_INFO, "client (%s) not responding",
|
||||
inet_ntoa(client_info->client.sin_addr));
|
||||
state = S_END;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The client failed to ACK our last packet. Send an
|
||||
OACK with mc=0 to it, fetch the next client in the
|
||||
list and continu with this new one */
|
||||
if (multicast && mcast_switch)
|
||||
{
|
||||
client_old = client_info;
|
||||
|
||||
tftpd_clientlist_next(data, &client_info);
|
||||
|
||||
if (client_info && (client_info != client_old))
|
||||
{
|
||||
/* Send an OACK to the old client remove is
|
||||
master client status */
|
||||
opt_options_to_string(options,
|
||||
string, MAXLEN);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent OACK <%s>", string);
|
||||
tftp_send_oack(sockfd, sa, options,
|
||||
data->data_buffer, data->data_buffer_size);
|
||||
|
||||
/* Proceed normally with the next client,
|
||||
going to OACK state */
|
||||
logger(LOG_INFO,
|
||||
"Serving next client: %s:%d",
|
||||
inet_ntoa(client_info->client.sin_addr),
|
||||
ntohs(client_info->client.sin_port));
|
||||
sa = &client_info->client;
|
||||
state = S_SEND_OACK;
|
||||
break;
|
||||
}
|
||||
else if (client_info == NULL)
|
||||
{
|
||||
/* we got a big problem if this happend */
|
||||
logger(LOG_ERR,
|
||||
"%s: %d: abnormal condition",
|
||||
__FILE__, __LINE__);
|
||||
state = S_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
logger(LOG_WARNING, "timeout: retrying...");
|
||||
state = timeout_state;
|
||||
}
|
||||
break;
|
||||
case GET_ACK:
|
||||
/* handle case where packet come from un unexpected client */
|
||||
if (multicast)
|
||||
{
|
||||
if ((sa->sin_port != from.sin_port) ||
|
||||
(sa->sin_addr.s_addr != from.sin_addr.s_addr))
|
||||
{
|
||||
/* We got an ACK from a client that is not the master client.
|
||||
* If this is an ACK for the last block, mark this client as
|
||||
* done
|
||||
*/
|
||||
if ((last_block != -1) && (block_number > last_block))
|
||||
{
|
||||
if (tftpd_clientlist_done(data, NULL, &from) == 1)
|
||||
logger(LOG_DEBUG, "client done <%s>",
|
||||
inet_ntoa(client_info->client.sin_addr));
|
||||
else
|
||||
logger(LOG_WARNING, "packet discarded <%s:%d>",
|
||||
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
|
||||
}
|
||||
else
|
||||
/* If not, send and OACK with mc=0 to shut it up. */
|
||||
{
|
||||
opt_options_to_string(options,
|
||||
string, MAXLEN);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent OACK <%s>", string);
|
||||
tftp_send_oack(sockfd, &from, options,
|
||||
data->data_buffer, data->data_buffer_size);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check that the packet is from the current client */
|
||||
if (sa->sin_port != from.sin_port)
|
||||
{
|
||||
if (data->checkport)
|
||||
{
|
||||
logger(LOG_WARNING, "packet discarded <%s:%d>",
|
||||
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
|
||||
break;
|
||||
}
|
||||
else
|
||||
logger(LOG_WARNING,
|
||||
"source port mismatch, check bypassed");
|
||||
}
|
||||
}
|
||||
/* The ACK is from the current client */
|
||||
number_of_timeout = 0;
|
||||
block_number = ntohs(tftphdr->th_block);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "received ACK <block: %d>",
|
||||
block_number);
|
||||
if ((last_block != -1) && (block_number > last_block))
|
||||
{
|
||||
state = S_END;
|
||||
break;
|
||||
}
|
||||
state = S_SEND_DATA;
|
||||
break;
|
||||
case GET_ERROR:
|
||||
/* handle case where packet come from un unexpected client */
|
||||
if (multicast)
|
||||
{
|
||||
/* if packet is not from the current master client */
|
||||
if ((sa->sin_port != from.sin_port) ||
|
||||
(sa->sin_addr.s_addr != from.sin_addr.s_addr))
|
||||
{
|
||||
/* mark this client done */
|
||||
if (tftpd_clientlist_done(data, NULL, &from) == 1)
|
||||
{
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "client sent ERROR, mark as done <%s>",
|
||||
inet_ntoa(client_info->client.sin_addr));
|
||||
}
|
||||
else
|
||||
logger(LOG_WARNING, "packet discarded <%s>",
|
||||
inet_ntoa(from.sin_addr));
|
||||
/* current state is unchanged */
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* check that the packet is from the current client */
|
||||
if (sa->sin_port != from.sin_port)
|
||||
{
|
||||
if (data->checkport)
|
||||
{
|
||||
logger(LOG_WARNING, "packet discarded <%s>",
|
||||
inet_ntoa(from.sin_addr));
|
||||
break;
|
||||
}
|
||||
else
|
||||
logger(LOG_WARNING,
|
||||
"source port mismatch, check bypassed");
|
||||
}
|
||||
}
|
||||
/* Got an ERROR from the current master client */
|
||||
Strncpy(string, tftphdr->th_msg,
|
||||
(((data_size - 4) > MAXLEN) ? MAXLEN :
|
||||
(data_size - 4)));
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "received ERROR <code: %d, msg: %s>",
|
||||
ntohs(tftphdr->th_code), string);
|
||||
if (multicast)
|
||||
{
|
||||
logger(LOG_DEBUG, "Marking client as done");
|
||||
state = S_END;
|
||||
}
|
||||
else
|
||||
state = S_ABORT;
|
||||
break;
|
||||
case GET_DISCARD:
|
||||
/* FIXME: should we increment number_of_timeout */
|
||||
logger(LOG_WARNING, "packet discarded <%s>",
|
||||
inet_ntoa(from.sin_addr));
|
||||
break;
|
||||
case ERR:
|
||||
logger(LOG_ERR, "%s: %d: recvfrom: %s",
|
||||
__FILE__, __LINE__, strerror(errno));
|
||||
state = S_ABORT;
|
||||
break;
|
||||
default:
|
||||
logger(LOG_ERR, "%s: %d: abnormal return value %d",
|
||||
__FILE__, __LINE__, result);
|
||||
}
|
||||
break;
|
||||
case S_END:
|
||||
if (multicast)
|
||||
{
|
||||
logger(LOG_DEBUG, "End of multicast transfer");
|
||||
/* mark the current client done */
|
||||
tftpd_clientlist_done(data, client_info, NULL);
|
||||
/* Look if there is another client to serve. We lock list of
|
||||
client to make sure no other thread try to add clients in
|
||||
our back */
|
||||
if (tftpd_clientlist_next(data, &client_info) == 1)
|
||||
{
|
||||
logger(LOG_INFO,
|
||||
"Serving next client: %s:%d",
|
||||
inet_ntoa(client_info->client.sin_addr),
|
||||
ntohs(client_info->client.sin_port));
|
||||
/* client is a new client structure */
|
||||
sa = &client_info->client;
|
||||
/* nedd to send an oack to that client */
|
||||
state = S_SEND_OACK;
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger(LOG_INFO, "No more client, end of tranfers");
|
||||
fclose(fp);
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger(LOG_DEBUG, "End of transfer");
|
||||
fclose(fp);
|
||||
return OK;
|
||||
}
|
||||
break;
|
||||
case S_ABORT:
|
||||
logger(LOG_DEBUG, "Aborting transfer");
|
||||
fclose(fp);
|
||||
return ERR;
|
||||
default:
|
||||
fclose(fp);
|
||||
logger(LOG_ERR, "%s: %d: abnormal condition",
|
||||
__FILE__, __LINE__);
|
||||
return ERR;
|
||||
}
|
||||
}
|
||||
}
|
350
src/bin/network/atftpd/tftpd_list.c
Normal file
350
src/bin/network/atftpd/tftpd_list.c
Normal file
@ -0,0 +1,350 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* tftpd_list.c
|
||||
* thread_data and client list related functions
|
||||
*
|
||||
* $Id: tftpd_list.c,v 1.9 2004/02/27 02:05:26 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include "tftpd.h"
|
||||
#include "logger.h"
|
||||
|
||||
/*
|
||||
* thread_data is a double link list of server threads. Server threads
|
||||
* are started by the main thread when a new request arrives. The number
|
||||
* of threads running is held in number_of_thread. Any access to the
|
||||
* thread_data list: insertion and extraction of elements must be protected
|
||||
* by the mutex. This is done in this file by all functions playing with the thread list.
|
||||
* Note that individual threads do not need to lock the list when playing in their data.
|
||||
* See tftpd.h.
|
||||
*
|
||||
* In addition, it is needed to use mutex when working on the client list of
|
||||
* individual thread. In some case, the thread data mutex is needed also.
|
||||
* Again, the functions in this file take care of this.
|
||||
*/
|
||||
|
||||
struct thread_data *thread_data = NULL; /* head of thread list */
|
||||
static int number_of_thread = 0;
|
||||
|
||||
pthread_mutex_t thread_list_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/*
|
||||
* Add a new thread_data structure to the list. Thread list mutex is locked
|
||||
* before walking the list and doing the insertion.
|
||||
*/
|
||||
int tftpd_list_add(struct thread_data *new)
|
||||
{
|
||||
struct thread_data *current = thread_data;
|
||||
int ret;
|
||||
|
||||
pthread_mutex_lock(&thread_list_mutex);
|
||||
|
||||
number_of_thread++;
|
||||
|
||||
ret = number_of_thread;
|
||||
|
||||
if (thread_data == NULL)
|
||||
{
|
||||
thread_data = new;
|
||||
new->prev = NULL;
|
||||
new->next = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (current->next != NULL)
|
||||
current = current->next;
|
||||
current->next = new;
|
||||
new->prev = current;
|
||||
new->next = NULL;
|
||||
}
|
||||
pthread_mutex_unlock(&thread_list_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a thread_data structure from the list.
|
||||
*/
|
||||
int tftpd_list_remove(struct thread_data *old)
|
||||
{
|
||||
struct thread_data *current = thread_data;
|
||||
int ret;
|
||||
|
||||
pthread_mutex_lock(&thread_list_mutex);
|
||||
|
||||
number_of_thread--;
|
||||
ret = number_of_thread;
|
||||
|
||||
if (thread_data == old)
|
||||
{
|
||||
if (thread_data->next != NULL)
|
||||
{
|
||||
thread_data = thread_data->next;
|
||||
thread_data->prev = NULL;
|
||||
}
|
||||
else
|
||||
thread_data = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (current != old)
|
||||
current = current->next;
|
||||
if (current->next && current->prev)
|
||||
{
|
||||
current->prev->next = current->next;
|
||||
current->next->prev = current->prev;
|
||||
}
|
||||
else
|
||||
current->prev->next = NULL;
|
||||
}
|
||||
pthread_mutex_unlock(&thread_list_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of threads actually started.
|
||||
*/
|
||||
int tftpd_list_num_of_thread(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pthread_mutex_lock(&thread_list_mutex);
|
||||
ret = number_of_thread;
|
||||
pthread_mutex_unlock(&thread_list_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function looks for a thread serving exactly the same
|
||||
* file and with the same options as another client. This implies a
|
||||
* multicast enabled client.
|
||||
*/
|
||||
int tftpd_list_find_multicast_server_and_add(struct thread_data **thread,
|
||||
struct thread_data *data,
|
||||
struct client_info *client)
|
||||
{
|
||||
struct thread_data *current = thread_data; /* head of the list */
|
||||
struct tftp_opt *tftp_options = data->tftp_options;
|
||||
struct client_info *tmp;
|
||||
char options[MAXLEN];
|
||||
char string[MAXLEN];
|
||||
char *index;
|
||||
int len;
|
||||
|
||||
*thread = NULL;
|
||||
|
||||
opt_request_to_string(tftp_options, options, MAXLEN);
|
||||
index = strstr(options, "multicast");
|
||||
len = (int)index - (int)options;
|
||||
|
||||
/* lock the whole list before walking it */
|
||||
pthread_mutex_lock(&thread_list_mutex);
|
||||
|
||||
while (current)
|
||||
{
|
||||
if (current != data)
|
||||
{
|
||||
/* Lock the client list before reading client ready state */
|
||||
pthread_mutex_lock(¤t->client_mutex);
|
||||
if (current->client_ready == 1)
|
||||
{
|
||||
opt_request_to_string(current->tftp_options, string, MAXLEN);
|
||||
/* must have exact same option string */
|
||||
if (strncmp(string, options, len) == 0)
|
||||
{
|
||||
*thread = current;
|
||||
/* insert the new client at the end. If the client is already
|
||||
in the list, don't add it again. */
|
||||
tmp = current->client_info;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if ((tmp->client.sin_port == client->client.sin_port) &&
|
||||
(tmp->client.sin_addr.s_addr == client->client.sin_addr.s_addr) &&
|
||||
(tmp->done == 0))
|
||||
{
|
||||
/* unlock mutex and exit */
|
||||
pthread_mutex_unlock(¤t->client_mutex);
|
||||
pthread_mutex_unlock(&thread_list_mutex);
|
||||
return 2;
|
||||
}
|
||||
if (tmp->next == NULL)
|
||||
break;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
tmp->next = client;
|
||||
/* unlock mutex and exit */
|
||||
pthread_mutex_unlock(¤t->client_mutex);
|
||||
pthread_mutex_unlock(&thread_list_mutex);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(¤t->client_mutex);
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
pthread_mutex_unlock(&thread_list_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void tftpd_clientlist_ready(struct thread_data *thread)
|
||||
{
|
||||
pthread_mutex_lock(&thread->client_mutex);
|
||||
thread->client_ready = 1;
|
||||
pthread_mutex_unlock(&thread->client_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* As of the the above comment, we never remove the head element.
|
||||
*/
|
||||
void tftpd_clientlist_remove(struct thread_data *thread,
|
||||
struct client_info *client)
|
||||
{
|
||||
struct client_info *tmp = thread->client_info;
|
||||
|
||||
pthread_mutex_lock(&thread->client_mutex);
|
||||
while ((tmp->next != client) && (tmp->next != NULL))
|
||||
tmp = tmp->next;
|
||||
if (tmp->next == NULL)
|
||||
return;
|
||||
tmp->next = tmp->next->next;
|
||||
pthread_mutex_unlock(&thread->client_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free all allocated client structure.
|
||||
*/
|
||||
void tftpd_clientlist_free(struct thread_data *thread)
|
||||
{
|
||||
struct client_info *tmp;
|
||||
struct client_info *head = thread->client_info;
|
||||
|
||||
pthread_mutex_lock(&thread->client_mutex);
|
||||
while (head)
|
||||
{
|
||||
tmp = head;
|
||||
head = head->next;
|
||||
free(tmp);
|
||||
}
|
||||
pthread_mutex_unlock(&thread->client_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark a client as done
|
||||
*/
|
||||
int tftpd_clientlist_done(struct thread_data *thread,
|
||||
struct client_info *client,
|
||||
struct sockaddr_in *sock)
|
||||
{
|
||||
struct client_info *head = thread->client_info;
|
||||
|
||||
pthread_mutex_lock(&thread->client_mutex);
|
||||
|
||||
if (client)
|
||||
{
|
||||
client->done = 1;
|
||||
pthread_mutex_unlock(&thread->client_mutex);
|
||||
return 1;
|
||||
}
|
||||
if (sock)
|
||||
{
|
||||
/* walk the list to find this client */
|
||||
while (head)
|
||||
{
|
||||
if (memcmp(sock, &head->client, sizeof(struct sockaddr_in)) == 0)
|
||||
{
|
||||
head->done = 1;
|
||||
pthread_mutex_unlock(&thread->client_mutex);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
head = head->next;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&thread->client_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the next client in the list.
|
||||
* If a client is found, *client got the address and return 1.
|
||||
* If no more client are available, 0 is returned and client_ready is unset
|
||||
* If the new client is the same, -1 is returned.
|
||||
*
|
||||
* List is search from the current client address and wrap around.
|
||||
*/
|
||||
int tftpd_clientlist_next(struct thread_data *thread,
|
||||
struct client_info **client)
|
||||
{
|
||||
struct client_info *tmp;
|
||||
|
||||
pthread_mutex_lock(&thread->client_mutex);
|
||||
|
||||
/* If there's a next, start with it. Else search from the
|
||||
beginning */
|
||||
if ((*client)->next)
|
||||
tmp = (*client)->next;
|
||||
else
|
||||
tmp = thread->client_info;
|
||||
|
||||
/* search from the current client */
|
||||
while (tmp)
|
||||
{
|
||||
if (!tmp->done)
|
||||
{
|
||||
/* If this is the same as *client */
|
||||
if (tmp == *client)
|
||||
{
|
||||
pthread_mutex_unlock(&thread->client_mutex);
|
||||
return -1;
|
||||
}
|
||||
*client = tmp;
|
||||
pthread_mutex_unlock(&thread->client_mutex);
|
||||
return 1;
|
||||
}
|
||||
tmp = tmp->next;
|
||||
/* if no more entry, start at begining */
|
||||
if (tmp == NULL)
|
||||
tmp = thread->client_info;
|
||||
/* if we fall back on the current client, there is no more
|
||||
client to serve */
|
||||
if (tmp == *client)
|
||||
tmp = NULL;
|
||||
}
|
||||
/* There is no more client to server */
|
||||
thread->client_ready = 0;
|
||||
*client = NULL;
|
||||
pthread_mutex_unlock(&thread->client_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tftpd_list_kill_threads(void)
|
||||
{
|
||||
struct thread_data *current = thread_data; /* head of list */
|
||||
|
||||
pthread_mutex_lock(&thread_list_mutex);
|
||||
|
||||
|
||||
while (current != NULL)
|
||||
{
|
||||
pthread_kill(current->tid, SIGTERM);
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&thread_list_mutex);
|
||||
}
|
351
src/bin/network/atftpd/tftpd_mcast.c
Normal file
351
src/bin/network/atftpd/tftpd_mcast.c
Normal file
@ -0,0 +1,351 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* tftpd_mcast.c
|
||||
* support routine for multicast server
|
||||
*
|
||||
* $Id: tftpd_mcast.c,v 1.6 2003/04/25 00:16:19 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include "tftpd.h"
|
||||
#include "tftp_def.h"
|
||||
#include "logger.h"
|
||||
|
||||
#define START 0
|
||||
#define GET_ENUM 1
|
||||
#define GET_GRP 2
|
||||
#define EXPAND 3
|
||||
|
||||
pthread_mutex_t mcast_tid_list = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
int parse_ip(char *string, char **res_ip);
|
||||
int parse_port(char *string, char **res_port);
|
||||
|
||||
struct tid {
|
||||
char *addr;
|
||||
short port;
|
||||
int used;
|
||||
struct tid *next;
|
||||
};
|
||||
|
||||
struct tid *tid_list = NULL;
|
||||
|
||||
/*
|
||||
* Return a free IP/Port for the multicast transfer
|
||||
*/
|
||||
int tftpd_mcast_get_tid(char **addr, short *port)
|
||||
{
|
||||
struct tid *current = tid_list;
|
||||
|
||||
pthread_mutex_lock(&mcast_tid_list);
|
||||
/* walk the list for a free tid */
|
||||
while (current != NULL)
|
||||
{
|
||||
if (current->used == 0)
|
||||
{
|
||||
*addr = current->addr;
|
||||
*port = current->port;
|
||||
current->used = 1;
|
||||
pthread_mutex_unlock(&mcast_tid_list);
|
||||
return OK;
|
||||
}
|
||||
else
|
||||
current = current->next;
|
||||
}
|
||||
pthread_mutex_unlock(&mcast_tid_list);
|
||||
return ERR;
|
||||
}
|
||||
|
||||
int tftpd_mcast_free_tid(char *addr, short port)
|
||||
{
|
||||
struct tid *current = tid_list;
|
||||
|
||||
pthread_mutex_lock(&mcast_tid_list);
|
||||
while (current != NULL)
|
||||
{
|
||||
if ((current->used == 1) && (current->port == port) &&
|
||||
(strcmp(current->addr, addr) == 0))
|
||||
{
|
||||
current->used = 0;
|
||||
pthread_mutex_unlock(&mcast_tid_list);
|
||||
return OK;
|
||||
}
|
||||
else
|
||||
current = current->next;
|
||||
}
|
||||
pthread_mutex_unlock(&mcast_tid_list);
|
||||
return ERR;
|
||||
}
|
||||
|
||||
/* valid address specification:
|
||||
239.255.0.0-239.255.0.20
|
||||
239.255.0.0-20
|
||||
239.255.0.0,1,2,3,8,10
|
||||
*/
|
||||
/* valid port specification
|
||||
1758
|
||||
1758-1780
|
||||
1758,1760,4000
|
||||
*/
|
||||
int tftpd_mcast_parse_opt(char *addr, char *ports)
|
||||
{
|
||||
char *ip;
|
||||
char *port;
|
||||
struct tid *current = NULL;
|
||||
struct tid *tmp = NULL;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (parse_ip(addr, &ip) != OK)
|
||||
return ERR;
|
||||
if (ip == NULL)
|
||||
return OK;
|
||||
while (1)
|
||||
{
|
||||
if (parse_port(ports, &port) != OK)
|
||||
return ERR;
|
||||
if (port == NULL)
|
||||
break;
|
||||
/* add this ip/port to the tid list */
|
||||
tmp = malloc(sizeof(struct tid));
|
||||
tmp->addr = strdup(ip);
|
||||
tmp->port = (short)atoi(port);
|
||||
tmp->used = 0;
|
||||
tmp->next = NULL;
|
||||
if (tid_list == NULL)
|
||||
{
|
||||
tid_list = tmp;
|
||||
current = tid_list;
|
||||
}
|
||||
else
|
||||
{
|
||||
current->next = tmp;
|
||||
current = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tftpd_mcast_clean(void)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
int parse_ip(char *string, char **res_ip)
|
||||
{
|
||||
static int state = START;
|
||||
|
||||
static char s[MAXLEN];
|
||||
static char *saveptr;
|
||||
static char s2[MAXLEN];
|
||||
static char *saveptr2;
|
||||
|
||||
static int ip[4];
|
||||
static int tmp_ip[4];
|
||||
|
||||
static int i;
|
||||
|
||||
int res;
|
||||
char *tmp = NULL, *tmp2 = NULL;
|
||||
static char out[MAXLEN];
|
||||
|
||||
*res_ip = NULL;
|
||||
|
||||
while (1)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case START:
|
||||
Strncpy(s, string, MAXLEN);
|
||||
tmp = strtok_r(s, ",", &saveptr);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
state = START;
|
||||
return ERR;
|
||||
}
|
||||
else
|
||||
state = GET_GRP;
|
||||
break;
|
||||
case GET_ENUM:
|
||||
tmp = strtok_r(NULL, ",", &saveptr);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
state = START;
|
||||
return OK;
|
||||
}
|
||||
else
|
||||
state = GET_GRP;
|
||||
break;
|
||||
case GET_GRP:
|
||||
Strncpy(s2, tmp, MAXLEN);
|
||||
tmp = strtok_r(s2, "-", &saveptr2);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
state = START;
|
||||
return ERR;
|
||||
}
|
||||
res = sscanf(tmp, "%d.%d.%d.%d", &tmp_ip[0], &tmp_ip[1],
|
||||
&tmp_ip[2], &tmp_ip[3]);
|
||||
if (res == 4)
|
||||
{
|
||||
for (i=0; i < 4; i++)
|
||||
ip[i] = tmp_ip[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (res == 1)
|
||||
{
|
||||
ip[3] = tmp_ip[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
state = START;
|
||||
return ERR;
|
||||
}
|
||||
}
|
||||
tmp2 = strtok_r(NULL, "-", &saveptr2);
|
||||
if (tmp2 == NULL)
|
||||
{
|
||||
state = GET_ENUM;
|
||||
snprintf(out, sizeof(out), "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
|
||||
*res_ip = out;
|
||||
return OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
sscanf(tmp2, "%d", &tmp_ip[0]);
|
||||
i = ip[3];
|
||||
if (i >= tmp_ip[0])
|
||||
{
|
||||
logger(LOG_ERR, "Bad address range: %d.%d.%d.%d-%d",
|
||||
ip[0], ip[1], ip[2], ip[3], tmp_ip[0]);
|
||||
return ERR;
|
||||
}
|
||||
state = EXPAND;
|
||||
}
|
||||
break;
|
||||
case EXPAND:
|
||||
if (i > tmp_ip[0])
|
||||
{
|
||||
state = GET_ENUM;
|
||||
break;
|
||||
}
|
||||
snprintf(out, sizeof(out), "%d.%d.%d.%d", ip[0], ip[1], ip[2], i);
|
||||
i++;
|
||||
*res_ip = out;
|
||||
return OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int parse_port(char *string, char **res_port)
|
||||
{
|
||||
static int state = START;
|
||||
|
||||
static char s[MAXLEN];
|
||||
static char *saveptr;
|
||||
static char s2[MAXLEN];
|
||||
static char *saveptr2;
|
||||
|
||||
static int port;
|
||||
static int tmp_port;
|
||||
|
||||
static int i;
|
||||
|
||||
int res;
|
||||
char *tmp = NULL, *tmp2 = NULL;
|
||||
static char out[MAXLEN];
|
||||
|
||||
*res_port = NULL;
|
||||
|
||||
while (1)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case START:
|
||||
Strncpy(s, string, MAXLEN);
|
||||
tmp = strtok_r(s, ",", &saveptr);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
state = START;
|
||||
return ERR;
|
||||
}
|
||||
else
|
||||
state = GET_GRP;
|
||||
break;
|
||||
case GET_ENUM:
|
||||
tmp = strtok_r(NULL, ",", &saveptr);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
state = START;
|
||||
return OK;
|
||||
}
|
||||
else
|
||||
state = GET_GRP;
|
||||
break;
|
||||
case GET_GRP:
|
||||
Strncpy(s2, tmp, MAXLEN);
|
||||
tmp = strtok_r(s2, "-", &saveptr2);
|
||||
if (tmp == NULL)
|
||||
{
|
||||
state = START;
|
||||
return ERR;
|
||||
}
|
||||
res = sscanf(tmp, "%d", &port);
|
||||
if (res != 1)
|
||||
{
|
||||
state = START;
|
||||
return ERR;
|
||||
}
|
||||
tmp2 = strtok_r(NULL, "-", &saveptr2);
|
||||
if (tmp2 == NULL)
|
||||
{
|
||||
state = GET_ENUM;
|
||||
snprintf(out, sizeof(out), "%d", port);
|
||||
*res_port = out;
|
||||
return OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
sscanf(tmp2, "%d", &tmp_port);
|
||||
i = port;
|
||||
if (i >= tmp_port)
|
||||
{
|
||||
logger(LOG_ERR, "Bad port range: %d-%d", i, tmp_port);
|
||||
return ERR;
|
||||
}
|
||||
state = EXPAND;
|
||||
}
|
||||
break;
|
||||
case EXPAND:
|
||||
if (i > tmp_port)
|
||||
{
|
||||
state = GET_ENUM;
|
||||
break;
|
||||
}
|
||||
snprintf(out, sizeof(out), "%d", i);
|
||||
i++;
|
||||
*res_port = out;
|
||||
return OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
678
src/bin/network/atftpd/tftpd_mtftp.c
Normal file
678
src/bin/network/atftpd/tftpd_mtftp.c
Normal file
@ -0,0 +1,678 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* tftpd_mtftp.c
|
||||
* mtftp support for atftp
|
||||
*
|
||||
* $Id: tftpd_mtftp.c,v 1.12 2004/02/27 02:05:26 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if HAVE_MTFTP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#ifdef HAVE_WRAP
|
||||
#include <tcpd.h>
|
||||
#endif
|
||||
#include "tftp_io.h"
|
||||
#include "tftp_def.h"
|
||||
#include "logger.h"
|
||||
#include "tftpd.h"
|
||||
#include "tftpd_mtftp.h"
|
||||
|
||||
#define S_BEGIN 0
|
||||
#define S_SEND_DATA 4
|
||||
#define S_WAIT_PACKET 5
|
||||
#define S_REQ_RECEIVED 6
|
||||
#define S_ABORT 10
|
||||
#define S_END 11
|
||||
#define S_EXIT 12
|
||||
|
||||
/* function used in this file only */
|
||||
int tftpd_mtftp_thread_add(struct mtftp_data *data, struct mtftp_thread *thread);
|
||||
struct mtftp_thread *tftpd_mtftp_find_server(struct mtftp_data *data, char *filename);
|
||||
int tftpd_mtftp_unique(struct mtftp_data *data, char *filename, char *ip, char *port);
|
||||
void *tftpd_mtftp_send_file(void *arg);
|
||||
|
||||
/* read only except for the main thread */
|
||||
extern int tftpd_cancel;
|
||||
|
||||
/*
|
||||
* This function parse the configuration file and create data structure
|
||||
* Format is:
|
||||
* filename mcast_ip client_port
|
||||
* one entry per line
|
||||
*/
|
||||
struct mtftp_data *tftpd_mtftp_init(char *filename)
|
||||
{
|
||||
FILE *fp;
|
||||
struct mtftp_data *data = NULL;
|
||||
struct mtftp_thread *thread = NULL;
|
||||
|
||||
char string[MAXLEN];
|
||||
char *token;
|
||||
|
||||
int line = 0;
|
||||
struct stat file_stat;
|
||||
struct hostent host;
|
||||
|
||||
/* open file */
|
||||
if ((fp = fopen(filename, "r")) == NULL)
|
||||
{
|
||||
logger(LOG_ERR, "mtftp: failed to open configuration file, continuing");
|
||||
return NULL;
|
||||
}
|
||||
logger(LOG_DEBUG, "mtftp: opened configuration file %s", filename);
|
||||
|
||||
/* allocate memory */
|
||||
if ((data = malloc(sizeof(struct mtftp_data))) == NULL)
|
||||
{
|
||||
logger(LOG_ERR, "%s: %d: Memory allocation failed",
|
||||
__FILE__, __LINE__);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
/* Allocate memory for tftp option structure. */
|
||||
if ((data->tftp_options =
|
||||
malloc(sizeof(tftp_default_options))) == NULL)
|
||||
{
|
||||
logger(LOG_ERR, "%s: %d: Memory allocation failed",
|
||||
__FILE__, __LINE__);
|
||||
tftpd_mtftp_clean(data);
|
||||
return NULL;
|
||||
}
|
||||
/* Allocate data buffer for tftp transfer. */
|
||||
if ((data->data_buffer = malloc((size_t)SEGSIZE + 4)) == NULL)
|
||||
{
|
||||
logger(LOG_ERR, "%s: %d: Memory allocation failed",
|
||||
__FILE__, __LINE__);
|
||||
tftpd_mtftp_clean(data);
|
||||
return NULL;
|
||||
}
|
||||
data->data_buffer_size = SEGSIZE + 4;
|
||||
|
||||
/* Copy default options. */
|
||||
memcpy(data->tftp_options, tftp_default_options,
|
||||
sizeof(tftp_default_options));
|
||||
data->tftp_options[OPT_TIMEOUT].enabled = 0;
|
||||
data->tftp_options[OPT_TSIZE].enabled = 0;
|
||||
data->tftp_options[OPT_BLKSIZE].enabled = 0;
|
||||
data->tftp_options[OPT_MULTICAST].enabled = 0;
|
||||
|
||||
/* init data structure */
|
||||
data->thread_data = NULL;
|
||||
data->number_of_thread = 0;
|
||||
|
||||
logger(LOG_DEBUG, "mtftp options: ");
|
||||
|
||||
/* parse the file */
|
||||
while (fgets(string, MAXLEN, fp) != NULL)
|
||||
{
|
||||
line++;
|
||||
|
||||
if (thread == NULL)
|
||||
{
|
||||
/* allocate memory */
|
||||
if ((thread = calloc(1, sizeof(struct mtftp_thread))) == NULL)
|
||||
{
|
||||
logger(LOG_ERR, "%s: %d: Memory allocation failed",
|
||||
__FILE__, __LINE__);
|
||||
fclose(fp);
|
||||
tftpd_mtftp_clean(data);
|
||||
return NULL;
|
||||
}
|
||||
/* Allocate data buffer for tftp transfer. */
|
||||
if ((thread->data_buffer = malloc((size_t)SEGSIZE + 4)) == NULL)
|
||||
{
|
||||
logger(LOG_ERR, "%s: %d: Memory allocation failed",
|
||||
__FILE__, __LINE__);
|
||||
tftpd_mtftp_clean(data);
|
||||
return NULL;
|
||||
}
|
||||
thread->data_buffer_size = SEGSIZE + 4;
|
||||
|
||||
/* Add this thread to the list */
|
||||
tftpd_mtftp_thread_add(data, thread);
|
||||
}
|
||||
|
||||
/* Parse the line */
|
||||
token = strtok(string, " ");
|
||||
if (!token)
|
||||
continue;
|
||||
if (token[0] == '#') /* if first char is #, this is a comment */
|
||||
continue;
|
||||
Strncpy(thread->file_name, token, MAXLEN);
|
||||
|
||||
token = strtok(NULL, " ");
|
||||
if (!token)
|
||||
continue;
|
||||
Strncpy(thread->mcast_ip, token, MAXLEN);
|
||||
|
||||
token = strtok(NULL, " ");
|
||||
if (!token)
|
||||
continue;
|
||||
Strncpy(thread->client_port, token, MAXLEN);
|
||||
|
||||
/* validate arguements */
|
||||
/* file name verification */
|
||||
if (tftpd_rules_check(thread->file_name) != OK)
|
||||
{
|
||||
logger(LOG_WARNING, "mtftp: file name rules violated %s (%s line %d)",
|
||||
thread->file_name, filename, line);
|
||||
continue;
|
||||
}
|
||||
/* open file */
|
||||
if ((thread->fp = fopen(thread->file_name, "r")) == NULL)
|
||||
{
|
||||
logger(LOG_WARNING, "mtftp: can't open file %s (%s line %d)",
|
||||
thread->file_name,
|
||||
filename, line);
|
||||
continue;
|
||||
}
|
||||
/* verify file size */
|
||||
fstat(fileno(thread->fp), &file_stat);
|
||||
if ((file_stat.st_size / SEGSIZE) > 65535)
|
||||
{
|
||||
logger(LOG_WARNING, "mtftp: file %s too big (%s line %d)", thread->file_name,
|
||||
filename, line);
|
||||
fclose(thread->fp);
|
||||
continue;
|
||||
}
|
||||
/* verify port */
|
||||
thread->mcast_port = atoi(thread->client_port);
|
||||
if ((thread->mcast_port < 0) || (thread->mcast_port > 65535))
|
||||
{
|
||||
logger(LOG_WARNING, "mtftp: bad port number %d (%s line %d)",
|
||||
thread->client_port, filename, line);
|
||||
fclose(thread->fp);
|
||||
continue;
|
||||
}
|
||||
/* verify IP is valid */
|
||||
if (Gethostbyname(thread->mcast_ip, &host) == OK)
|
||||
{
|
||||
thread->sa_mcast.sin_family = host.h_addrtype;
|
||||
memcpy(&thread->sa_mcast.sin_addr.s_addr,
|
||||
host.h_addr_list[0], host.h_length);
|
||||
thread->sa_mcast.sin_port = htons(thread->mcast_port);
|
||||
if (!IN_MULTICAST(ntohl(thread->sa_mcast.sin_addr.s_addr)))
|
||||
{
|
||||
logger(LOG_WARNING, "mtftp: bad multicast address %s\n",
|
||||
thread->mcast_ip);
|
||||
fclose(thread->fp);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/* verify IP/port is unique */
|
||||
if (tftpd_mtftp_unique(data, thread->file_name, thread->mcast_ip, thread->client_port))
|
||||
{
|
||||
logger(LOG_INFO, "mtftp: duplicate server (%s line %d)", filename, line);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* some useful info */
|
||||
logger(LOG_INFO, "mtftp: will serve %s on %s port %s", thread->file_name,
|
||||
thread->mcast_ip, thread->client_port);
|
||||
/* next loop we must allocate a new structure */
|
||||
thread = NULL;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return data;
|
||||
}
|
||||
|
||||
int tftpd_mtftp_clean(struct mtftp_data *data)
|
||||
{
|
||||
struct mtftp_thread *thread = data->thread_data;
|
||||
struct mtftp_thread *tmp = data->thread_data;
|
||||
|
||||
if (data == NULL)
|
||||
return OK;
|
||||
|
||||
/* clean all thread data structure */
|
||||
while (thread)
|
||||
{
|
||||
/* point to the next item before freeing memory */
|
||||
tmp = thread->next;
|
||||
/* free everything */
|
||||
if (thread->fp)
|
||||
fclose(thread->fp);
|
||||
if (thread->data_buffer)
|
||||
free(thread->data_buffer);
|
||||
free(thread);
|
||||
thread = tmp;
|
||||
}
|
||||
/* clean mtftp_data structure */
|
||||
if (data->data_buffer)
|
||||
free(data->data_buffer);
|
||||
if (data->tftp_options)
|
||||
free(data->tftp_options);
|
||||
free(data);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a mtftp_thread structure to the list. I guest this could use the code in
|
||||
* tftpd_list.c, but I prefer keeping it appart for now.
|
||||
*/
|
||||
int tftpd_mtftp_thread_add(struct mtftp_data *data, struct mtftp_thread *thread)
|
||||
{
|
||||
struct mtftp_thread *tmp = data->thread_data;
|
||||
|
||||
if (data->thread_data == NULL)
|
||||
{
|
||||
data->thread_data = thread;
|
||||
return OK;
|
||||
}
|
||||
while (tmp->next != NULL)
|
||||
tmp = tmp->next;
|
||||
tmp->next = thread;
|
||||
return OK;
|
||||
}
|
||||
|
||||
void tftpd_mtftp_kill_threads(struct mtftp_data *data)
|
||||
{
|
||||
struct mtftp_thread *current = data->thread_data; /* head of list */
|
||||
|
||||
while (current != NULL)
|
||||
{
|
||||
/* kill running threads */
|
||||
if (current->running && current->tid)
|
||||
pthread_kill(current->tid, SIGTERM);
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
struct mtftp_thread *tftpd_mtftp_find_server(struct mtftp_data *data, char *filename)
|
||||
{
|
||||
struct mtftp_thread *tmp = data->thread_data;
|
||||
|
||||
while (tmp)
|
||||
{
|
||||
if (strcmp(filename, tmp->file_name) == 0)
|
||||
return tmp;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int tftpd_mtftp_unique(struct mtftp_data *data, char *filename, char *ip, char *port)
|
||||
{
|
||||
struct mtftp_thread *tmp = data->thread_data;
|
||||
|
||||
while (tmp->next)
|
||||
{
|
||||
if (strcmp(filename, tmp->file_name) == 0)
|
||||
return 1;
|
||||
if (strcmp(ip, tmp->mcast_ip) == 0)
|
||||
if (strcmp(port, tmp->client_port) == 0)
|
||||
return 1;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This thread listen to the specified port for read request. If the requested
|
||||
* file as been specified in the mtftp.conf file and the server is not currently
|
||||
* serving this file, spawn the serving thread.
|
||||
*/
|
||||
void *tftpd_mtftp_server(void *arg)
|
||||
{
|
||||
fd_set rfds; /* for select */
|
||||
struct mtftp_data *data = (struct mtftp_data *)arg;
|
||||
struct mtftp_thread *thread;
|
||||
|
||||
int sockfd;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t len = sizeof(struct sockaddr);
|
||||
#ifdef HAVE_WRAP
|
||||
char client_addr[16];
|
||||
#endif
|
||||
int retval; /* hold return value for testing */
|
||||
int data_size; /* returned size by recvfrom */
|
||||
char filename[MAXLEN];
|
||||
char string[MAXLEN]; /* hold the string we pass to the logger */
|
||||
|
||||
logger(LOG_NOTICE, "mtftp main server thread started");
|
||||
|
||||
/* initialise sockaddr_in structure */
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_family = AF_INET;
|
||||
sa.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
sa.sin_port = htons(data->server_port);
|
||||
|
||||
/* open the socket */
|
||||
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
|
||||
{
|
||||
logger(LOG_ERR, "mtftp: can't open socket");
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
/* bind the socket to the tftp port */
|
||||
if (bind(sockfd, (struct sockaddr*)&sa, sizeof(sa)) < 0)
|
||||
{
|
||||
logger(LOG_ERR, "mtftp: can't bind port");
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
while (!tftpd_cancel)
|
||||
{
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(sockfd, &rfds);
|
||||
|
||||
select(sockfd + 1, &rfds, NULL, NULL, NULL);
|
||||
|
||||
if (FD_ISSET(sockfd, &rfds) && (!tftpd_cancel))
|
||||
{
|
||||
/* read the data packet and verify it's a RRQ and a thread exist for
|
||||
that file name */
|
||||
memset(&sa, 0, sizeof(sa)); /* this will hold the client info */
|
||||
data_size = data->data_buffer_size;
|
||||
retval = tftp_get_packet(sockfd, -1, NULL, &sa, NULL, NULL,
|
||||
data->timeout,
|
||||
&data_size, data->data_buffer);
|
||||
|
||||
#ifdef HAVE_WRAP
|
||||
/* Verify the client has access. We don't look for the name but
|
||||
rely only on the IP address for that. */
|
||||
inet_ntop(AF_INET, &sa.sin_addr,
|
||||
client_addr, sizeof(client_addr));
|
||||
if (hosts_ctl("in.tftpd", STRING_UNKNOWN, client_addr,
|
||||
STRING_UNKNOWN) == 0)
|
||||
{
|
||||
logger(LOG_ERR, "mtftp: connection refused from %s", client_addr);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
/* read options from this request */
|
||||
opt_parse_request(data->data_buffer, data_size,
|
||||
data->tftp_options);
|
||||
opt_request_to_string(data->tftp_options, string, MAXLEN);
|
||||
Strncpy(filename, data->tftp_options[OPT_FILENAME].value,
|
||||
MAXLEN);
|
||||
/* verify this is a RRQ */
|
||||
if (retval != GET_RRQ)
|
||||
{
|
||||
logger(LOG_WARNING, "unsupported request <%d> from %s",
|
||||
retval, inet_ntoa(sa.sin_addr));
|
||||
tftp_send_error(sockfd, &sa, EBADOP, data->data_buffer, data->data_buffer_size);
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EBADOP,
|
||||
tftp_errmsg[EBADOP]);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger(LOG_NOTICE, "Serving %s to %s:%d", filename,
|
||||
inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
|
||||
if (data->trace)
|
||||
logger(LOG_DEBUG, "received RRQ <%s>", string);
|
||||
}
|
||||
/* validity check, only octet mode supported */
|
||||
if (strcasecmp(data->tftp_options[OPT_MODE].value, "octet") != 0)
|
||||
{
|
||||
logger(LOG_WARNING, "mtftp: support only octet mode");
|
||||
continue;
|
||||
}
|
||||
/* file name verification */
|
||||
if (tftpd_rules_check(filename) != OK)
|
||||
{
|
||||
logger(LOG_WARNING, "mtftp: file name rules violated %s", filename);
|
||||
continue;
|
||||
}
|
||||
/* find server for this file*/
|
||||
if ((thread = tftpd_mtftp_find_server(data, filename)) == NULL)
|
||||
{
|
||||
logger(LOG_WARNING, "mtftp: no server found for file %s", filename);
|
||||
continue;
|
||||
}
|
||||
if (thread->running)
|
||||
{
|
||||
logger(LOG_NOTICE, "mtftp: already serving this file");
|
||||
continue;
|
||||
}
|
||||
/* copy client info for server */
|
||||
memcpy(&thread->sa_in, &sa, sizeof(struct sockaddr_in));
|
||||
/* open a socket for client communication */
|
||||
if ((thread->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
|
||||
{
|
||||
logger(LOG_ERR, "mtftp: can't open socket");
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
getsockname(sockfd, (struct sockaddr *)&(sa), &len);
|
||||
//memset(&sa, 0, sizeof(sa));
|
||||
sa.sin_port = 0;
|
||||
/* bind the socket to the tftp port */
|
||||
if (bind(thread->sockfd, (struct sockaddr*)&sa, sizeof(sa)) < 0)
|
||||
{
|
||||
logger(LOG_ERR, "mtftp: can't bind port");
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
getsockname(thread->sockfd, (struct sockaddr *)&(sa), &len);
|
||||
|
||||
/* configure multicast socket */
|
||||
thread->mcastaddr.imr_multiaddr.s_addr = thread->sa_mcast.sin_addr.s_addr;
|
||||
thread->mcastaddr.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||
setsockopt(thread->sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
|
||||
&data->mcast_ttl, sizeof(data->mcast_ttl));
|
||||
|
||||
/* give server thread access to mtftp options */
|
||||
thread->mtftp_data = data;
|
||||
|
||||
/* mask the thread as running */
|
||||
thread->running = 1;
|
||||
|
||||
/* spawn the new thread */
|
||||
/* Start a new server thread. */
|
||||
if (pthread_create(&thread->tid, NULL, tftpd_mtftp_send_file,
|
||||
(void *)thread) != 0)
|
||||
{
|
||||
logger(LOG_ERR, "mtftp: failed to start new thread");
|
||||
thread->running = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tftpd_mtftp_clean(data);
|
||||
|
||||
logger(LOG_NOTICE, "mtftp main server thread exiting");
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void *tftpd_mtftp_send_file(void *arg)
|
||||
{
|
||||
int state = S_BEGIN;
|
||||
int timeout_state = state;
|
||||
int result;
|
||||
int block_number = 0;
|
||||
int last_block = -1;
|
||||
int data_size;
|
||||
|
||||
struct mtftp_thread *data = (struct mtftp_thread *)arg;
|
||||
struct sockaddr_in *sa = &data->sa_in;
|
||||
struct sockaddr_in from;
|
||||
int sockfd = data->sockfd;
|
||||
|
||||
struct tftphdr *tftphdr = (struct tftphdr *)data->data_buffer;
|
||||
char string[MAXLEN];
|
||||
int number_of_timeout = 0;
|
||||
|
||||
/* Detach ourself. That way the main thread does not have to
|
||||
* wait for us with pthread_join. */
|
||||
pthread_detach(pthread_self());
|
||||
|
||||
/* sockets are opened and every as been initialised for us,
|
||||
just proceed */
|
||||
while (1)
|
||||
{
|
||||
if (tftpd_cancel)
|
||||
{
|
||||
logger(LOG_DEBUG, "thread cancelled");
|
||||
tftp_send_error(sockfd, sa, EUNDEF, data->data_buffer, data->data_buffer_size);
|
||||
if (data->mtftp_data->trace)
|
||||
logger(LOG_DEBUG, "sent ERROR <code: %d, msg: %s>", EUNDEF,
|
||||
tftp_errmsg[EUNDEF]);
|
||||
state = S_ABORT;
|
||||
}
|
||||
switch (state)
|
||||
{
|
||||
case S_BEGIN:
|
||||
/* The first data packet as to be sent to the unicast address
|
||||
of the client */
|
||||
timeout_state = state;
|
||||
fseek(data->fp, block_number * (data->data_buffer_size - 4),
|
||||
SEEK_SET);
|
||||
/* read data from file */
|
||||
data_size = fread(tftphdr->th_data, 1,
|
||||
data->data_buffer_size - 4, data->fp) + 4;
|
||||
/* record the last block number */
|
||||
if (feof(data->fp))
|
||||
last_block = block_number;
|
||||
/* send data to unicast address */
|
||||
tftp_send_data(sockfd, sa, block_number + 1,
|
||||
data_size, data->data_buffer);
|
||||
if (data->mtftp_data->trace)
|
||||
logger(LOG_DEBUG, "sent DATA <block: %d, size %d>",
|
||||
block_number + 1, data_size - 4);
|
||||
state = S_WAIT_PACKET;
|
||||
break;
|
||||
case S_SEND_DATA:
|
||||
timeout_state = state;
|
||||
fseek(data->fp, block_number * (data->data_buffer_size - 4),
|
||||
SEEK_SET);
|
||||
/* read data from file */
|
||||
data_size = fread(tftphdr->th_data, 1,
|
||||
data->data_buffer_size - 4, data->fp) + 4;
|
||||
/* record the last block number */
|
||||
if (feof(data->fp))
|
||||
last_block = block_number;
|
||||
/* send data to multicast address */
|
||||
tftp_send_data(sockfd, &data->sa_mcast,
|
||||
block_number + 1, data_size,
|
||||
data->data_buffer);
|
||||
if (data->mtftp_data->trace)
|
||||
logger(LOG_DEBUG, "sent DATA <block: %d, size %d>",
|
||||
block_number + 1, data_size - 4);
|
||||
state = S_WAIT_PACKET;
|
||||
break;
|
||||
case S_WAIT_PACKET:
|
||||
data_size = data->data_buffer_size;
|
||||
result = tftp_get_packet(sockfd, -1, NULL, sa, &from, NULL,
|
||||
data->mtftp_data->timeout,
|
||||
&data_size, data->data_buffer);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case GET_TIMEOUT:
|
||||
number_of_timeout++;
|
||||
|
||||
if (number_of_timeout > NB_OF_RETRY)
|
||||
{
|
||||
logger(LOG_INFO, "client (%s) not responding",
|
||||
inet_ntoa(data->sa_in.sin_addr));
|
||||
state = S_END;
|
||||
break;
|
||||
}
|
||||
logger(LOG_WARNING, "timeout: retrying...");
|
||||
state = timeout_state;
|
||||
break;
|
||||
case GET_ACK:
|
||||
if (sa->sin_port != from.sin_port)
|
||||
{
|
||||
logger(LOG_WARNING, "packet discarded");
|
||||
break;
|
||||
}
|
||||
/* handle case where packet come from un unexpected client */
|
||||
if ((sa->sin_port == from.sin_port) &&
|
||||
(sa->sin_addr.s_addr == from.sin_addr.s_addr))
|
||||
{
|
||||
/* The ACK is from the exected client */
|
||||
number_of_timeout = 0;
|
||||
block_number = ntohs(tftphdr->th_block);
|
||||
if (data->mtftp_data->trace)
|
||||
logger(LOG_DEBUG, "received ACK <block: %d>",
|
||||
block_number);
|
||||
if ((last_block != -1) && (block_number > last_block))
|
||||
{
|
||||
state = S_END;
|
||||
break;
|
||||
}
|
||||
state = S_SEND_DATA;
|
||||
}
|
||||
break;
|
||||
case GET_ERROR:
|
||||
if (sa->sin_port != from.sin_port)
|
||||
{
|
||||
logger(LOG_WARNING, "packet discarded");
|
||||
break;
|
||||
}
|
||||
/* handle case where packet come from un unexpected client */
|
||||
if ((sa->sin_port == from.sin_port) &&
|
||||
(sa->sin_addr.s_addr == from.sin_addr.s_addr))
|
||||
{
|
||||
/* Got an ERROR from the current master client */
|
||||
Strncpy(string, tftphdr->th_msg,
|
||||
(((data_size - 4) > MAXLEN) ? MAXLEN :
|
||||
(data_size - 4)));
|
||||
if (data->mtftp_data->trace)
|
||||
logger(LOG_DEBUG, "received ERROR <code: %d, msg: %s>",
|
||||
ntohs(tftphdr->th_code), string);
|
||||
state = S_ABORT;
|
||||
}
|
||||
break;
|
||||
case GET_DISCARD:
|
||||
logger(LOG_WARNING, "packet discarded");
|
||||
break;
|
||||
case ERR:
|
||||
logger(LOG_ERR, "%s: %d: recvfrom: %s",
|
||||
__FILE__, __LINE__, strerror(errno));
|
||||
state = S_ABORT;
|
||||
break;
|
||||
default:
|
||||
logger(LOG_ERR, "%s: %d: abnormal return value %d",
|
||||
__FILE__, __LINE__, result);
|
||||
}
|
||||
break;
|
||||
case S_END:
|
||||
logger(LOG_DEBUG, "End of transfer");
|
||||
state = S_EXIT;
|
||||
break;
|
||||
case S_ABORT:
|
||||
logger(LOG_DEBUG, "Aborting transfer");
|
||||
state = S_EXIT;
|
||||
break;
|
||||
case S_EXIT:
|
||||
data->running = 0;
|
||||
data->tid = 0;
|
||||
pthread_exit(NULL);
|
||||
default:
|
||||
logger(LOG_ERR, "%s: %d: abnormal condition",
|
||||
__FILE__, __LINE__);
|
||||
state = S_EXIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
85
src/bin/network/atftpd/tftpd_mtftp.h
Normal file
85
src/bin/network/atftpd/tftpd_mtftp.h
Normal file
@ -0,0 +1,85 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* tftpd_mtftp.h
|
||||
*
|
||||
* $Id: tftpd_mtftp.h,v 1.5 2004/02/27 02:05:26 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef tftpd_mtftp_h
|
||||
#define tftpd_mtftp_h
|
||||
|
||||
#include <pthread.h>
|
||||
#include <arpa/tftp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "tftp_io.h"
|
||||
#include "options.h"
|
||||
|
||||
/*
|
||||
* This structure hold information on mtftp configuration
|
||||
*
|
||||
*/
|
||||
struct mtftp_data {
|
||||
struct mtftp_thread *thread_data;
|
||||
int number_of_thread;
|
||||
|
||||
/* for receiving of initial request */
|
||||
char *data_buffer;
|
||||
int data_buffer_size;
|
||||
struct tftp_opt *tftp_options;
|
||||
|
||||
/* options scanned from command line */
|
||||
int server_port;
|
||||
int mcast_ttl;
|
||||
int timeout;
|
||||
int checkport;
|
||||
int trace;
|
||||
};
|
||||
|
||||
struct mtftp_thread {
|
||||
pthread_t tid;
|
||||
|
||||
/* Configuration data */
|
||||
int running;
|
||||
char file_name[MAXLEN];
|
||||
char mcast_ip[MAXLEN]; /* FIXME: could be less memory */
|
||||
char client_port[MAXLEN];
|
||||
|
||||
/* Server thread variables */
|
||||
FILE *fp;
|
||||
|
||||
int sockfd;
|
||||
struct sockaddr_in sa_in;
|
||||
struct sockaddr_in sa_client;
|
||||
|
||||
int mcast_sockfd;
|
||||
int mcast_port;
|
||||
struct sockaddr_in sa_mcast;
|
||||
struct ip_mreq mcastaddr;
|
||||
|
||||
char *data_buffer;
|
||||
int data_buffer_size;
|
||||
|
||||
/* For options access */
|
||||
struct mtftp_data *mtftp_data;
|
||||
|
||||
struct mtftp_thread *next;
|
||||
};
|
||||
|
||||
/*
|
||||
* Functions defined in tftpd_file.c
|
||||
*/
|
||||
struct mtftp_data *tftpd_mtftp_init(char *filename);
|
||||
int tftpd_mtftp_clean(struct mtftp_data *data);
|
||||
void *tftpd_mtftp_server(void *arg);
|
||||
void tftpd_mtftp_kill_threads(struct mtftp_data *data);
|
||||
|
||||
#endif
|
315
src/bin/network/atftpd/tftpd_pcre.c
Normal file
315
src/bin/network/atftpd/tftpd_pcre.c
Normal file
@ -0,0 +1,315 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* tftpd_pcre.c
|
||||
* functions to remmap file name requested by tftp clients according
|
||||
* to regular expression rules
|
||||
*
|
||||
* $Id: tftpd_pcre.c,v 1.2 2003/04/25 00:16:19 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2003 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* The PCRE code is provided by Jeff Miller <jeff.miller@transact.com.au>
|
||||
*
|
||||
* Copyright (c) 2003 Jeff Miller <jeff.miller@transact.com.au>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if HAVE_PCRE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "tftp_def.h"
|
||||
#include "config.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include "tftpd_pcre.h"
|
||||
|
||||
|
||||
/*
|
||||
* number of elements in vector to hold substring info
|
||||
*/
|
||||
#define OVECCOUNT 30
|
||||
|
||||
/*
|
||||
* define the pattern for substitutions
|
||||
* $0 whole string
|
||||
* $1 to $9 substring 1 - 9
|
||||
*/
|
||||
|
||||
/* create a pattern list from a file */
|
||||
/* return 0 on success, -1 otherwise */
|
||||
tftpd_pcre_self_t *tftpd_pcre_open(char *filename)
|
||||
{
|
||||
int linecount;
|
||||
int erroffset;
|
||||
int matches;
|
||||
int ovector[OVECCOUNT];
|
||||
char line[MAXLEN];
|
||||
const char *error;
|
||||
FILE *fh;
|
||||
int subnum;
|
||||
char **substrlist;
|
||||
pcre *file_re;
|
||||
pcre_extra *file_pe;
|
||||
tftpd_pcre_self_t *self;
|
||||
tftpd_pcre_pattern_t *pat, **curpatp;
|
||||
|
||||
/* open file */
|
||||
if ((fh = fopen(filename, "r")) == NULL)
|
||||
{
|
||||
logger(LOG_ERR, "Cannot open %s for reading: %s",
|
||||
filename, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* compile and study pattern for lines */
|
||||
logger(LOG_DEBUG, "Using file pattern %s", TFTPD_PCRE_FILE_PATTERN);
|
||||
if ((file_re = pcre_compile(TFTPD_PCRE_FILE_PATTERN, 0,
|
||||
&error, &erroffset, NULL)) == NULL)
|
||||
{
|
||||
logger(LOG_ERR, "PCRE file pattern failed to compile");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file_pe = pcre_study(file_re, 0, &error);
|
||||
if (error != NULL)
|
||||
{
|
||||
logger(LOG_ERR, "PCRE file pattern failed to study");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* allocate header and copy info */
|
||||
if ((self = calloc(1, sizeof(tftpd_pcre_self_t))) == NULL)
|
||||
{
|
||||
logger(LOG_ERR, "calloc filed");
|
||||
return NULL;
|
||||
}
|
||||
self->lock = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
|
||||
Strncpy(self->filename, filename, MAXLEN);
|
||||
|
||||
/* read patterns */
|
||||
for (linecount = 1, curpatp = &self->list;
|
||||
fgets(line, MAXLEN, fh) != NULL;
|
||||
linecount++, curpatp = &pat->next)
|
||||
{
|
||||
logger(LOG_DEBUG,"file: %s line: %d value: %s",
|
||||
filename, linecount, line);
|
||||
|
||||
/* allocate space for pattern info */
|
||||
if ((pat = (tftpd_pcre_pattern_t *)calloc(1,sizeof(tftpd_pcre_pattern_t))) == NULL)
|
||||
{
|
||||
tftpd_pcre_close(self);
|
||||
return NULL;
|
||||
}
|
||||
*curpatp = pat;
|
||||
|
||||
/* for each pattern read, compile and store the pattern */
|
||||
matches = pcre_exec(file_re, file_pe, line, (int)(strlen(line)),
|
||||
0, 0, ovector, OVECCOUNT);
|
||||
/* log substring to help with debugging */
|
||||
pcre_get_substring_list(line, ovector, matches, (const char ***)&substrlist);
|
||||
for(subnum = 0; subnum <= matches; subnum++)
|
||||
{
|
||||
logger(LOG_DEBUG,"file: %s line: %d substring: %d value: %s",
|
||||
filename, linecount, subnum, substrlist[subnum]);
|
||||
}
|
||||
pcre_free_substring_list((const char **)substrlist);
|
||||
|
||||
if (matches < 2)
|
||||
{
|
||||
logger(LOG_ERR, "error with pattern in file \"%s\" line %d",
|
||||
filename, linecount);
|
||||
tftpd_pcre_close(self);
|
||||
pcre_free(file_re);
|
||||
pcre_free(file_pe);
|
||||
return NULL;
|
||||
}
|
||||
/* remember line number */
|
||||
pat->linenum = linecount;
|
||||
|
||||
/* extract left side */
|
||||
pcre_get_substring(line, ovector, matches, 1, (const char **)&pat->pattern);
|
||||
|
||||
/* extract right side */
|
||||
pcre_get_substring(line, ovector, matches, 2, (const char **)&pat->right_str);
|
||||
logger(LOG_DEBUG,"pattern: %s right_str: %s", pat->pattern, pat->right_str);
|
||||
|
||||
if ((pat->left_re = pcre_compile(pat->pattern, 0,
|
||||
&error, &erroffset, NULL)) == NULL)
|
||||
{
|
||||
/* compilation failed*/
|
||||
logger(LOG_ERR,
|
||||
"PCRE compilation failed in file \"%s\" line %d at %d: %s",
|
||||
filename, linecount,
|
||||
erroffset, error);
|
||||
/* close file */
|
||||
fclose(fh);
|
||||
/* clean up */
|
||||
tftpd_pcre_close(self);
|
||||
return NULL;
|
||||
}
|
||||
/* we're going to be using this pattern a fair bit so lets study it */
|
||||
pat->left_pe = pcre_study(pat->left_re, 0, &error);
|
||||
if (error != NULL)
|
||||
{
|
||||
logger(LOG_ERR,
|
||||
"PCRE study failed in file \"%s\" line %d: %s",
|
||||
filename, linecount,
|
||||
error);
|
||||
/* close file */
|
||||
fclose(fh);
|
||||
/* cleanup */
|
||||
tftpd_pcre_close(self);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* clean up */
|
||||
pcre_free(file_re);
|
||||
pcre_free(file_pe);
|
||||
/* close file */
|
||||
fclose(fh);
|
||||
return self;
|
||||
}
|
||||
|
||||
/* return filename being used */
|
||||
/* returning a char point directly is a little risking when
|
||||
* using thread, but as we're using this before threads
|
||||
* are created we should be able to get away with it
|
||||
*/
|
||||
char *tftpd_pcre_getfilename(tftpd_pcre_self_t *self)
|
||||
{
|
||||
return self->filename;
|
||||
}
|
||||
|
||||
/* this is a utility function used to make the actual substitution*/
|
||||
int tftpd_pcre_makesub(struct tftpd_pcre_pattern *pat,
|
||||
char *outstr, int outsize,
|
||||
char *str,
|
||||
int *ovector, int matches)
|
||||
{
|
||||
char *chp, *outchp;
|
||||
const char *tmpstr;
|
||||
int rc;
|
||||
|
||||
/* $0 - whole string, $1-$9 substring 1-9 */
|
||||
for (chp = pat->right_str, outchp = outstr;
|
||||
(*chp != '\0') && (outchp - outstr < outsize);
|
||||
chp++)
|
||||
{
|
||||
if ((*chp == '$') && (*(chp+1) >= '0') && (*(chp+1) <= '9'))
|
||||
{
|
||||
chp++; /* point to value indicating substring */
|
||||
rc = pcre_get_substring(str, ovector, matches, *chp - 0x30, &tmpstr);
|
||||
/* found string */
|
||||
if (rc > 0)
|
||||
{
|
||||
Strncpy(outchp, tmpstr, rc);
|
||||
outchp += rc;
|
||||
pcre_free_substring(tmpstr);
|
||||
continue;
|
||||
}
|
||||
/* erro condition */
|
||||
switch (rc)
|
||||
{
|
||||
case PCRE_ERROR_NOMEMORY:
|
||||
logger(LOG_ERR, "PCRE out of memory");
|
||||
break;
|
||||
case PCRE_ERROR_NOSUBSTRING:
|
||||
logger(LOG_ERR,
|
||||
"PCRE attempted substitution failed for \"%s\" on pattern %d",
|
||||
str, pat->linenum);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*outchp = *chp;
|
||||
outchp++;
|
||||
}
|
||||
}
|
||||
*outchp = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* search for a replacement and return a string after substituation */
|
||||
/* if no match is found return -1 */
|
||||
int tftpd_pcre_sub(tftpd_pcre_self_t *self, char *outstr, int outlen, char *str)
|
||||
{
|
||||
int rc;
|
||||
int ovector[OVECCOUNT];
|
||||
int matches;
|
||||
tftpd_pcre_pattern_t *pat;
|
||||
|
||||
/* lock for duration */
|
||||
pthread_mutex_lock(&self->lock);
|
||||
|
||||
logger(LOG_DEBUG, "Looking to match \"%s\"", str);
|
||||
/* interate over pattern list */
|
||||
for(pat = self->list; pat != NULL; pat = pat->next)
|
||||
{
|
||||
logger(LOG_DEBUG,"Attempting to match \"%s\"", pat->pattern);
|
||||
/* attempt match */
|
||||
matches = pcre_exec(pat->left_re, pat->left_pe,
|
||||
str, (int)(strlen(str)),
|
||||
0, 0,
|
||||
ovector, OVECCOUNT);
|
||||
|
||||
/* no match so we try again */
|
||||
if (matches == PCRE_ERROR_NOMATCH)
|
||||
continue;
|
||||
/* error in making a match - log and attempt to continue */
|
||||
if (matches < 0)
|
||||
{
|
||||
logger(LOG_WARNING,
|
||||
"PCRE Matching error %d", matches);
|
||||
continue;
|
||||
}
|
||||
/* we have a match - carry out substitution */
|
||||
logger(LOG_DEBUG,"Pattern \"%s\" matches", pat->pattern);
|
||||
rc = tftpd_pcre_makesub(pat,
|
||||
outstr, outlen,
|
||||
str,
|
||||
ovector, matches);
|
||||
logger(LOG_DEBUG,"outstr: \"%s\"", outstr);
|
||||
pthread_mutex_unlock(&self->lock);
|
||||
return 0;
|
||||
}
|
||||
logger(LOG_DEBUG, "Failed to match \"%s\"", str);
|
||||
pthread_mutex_unlock(&self->lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* clean up and displose of anything we set up*/
|
||||
void tftpd_pcre_close(tftpd_pcre_self_t *self)
|
||||
{
|
||||
tftpd_pcre_pattern_t *next, *cur;
|
||||
|
||||
/* free up list */
|
||||
pthread_mutex_lock(&self->lock);
|
||||
|
||||
cur = self->list;
|
||||
while (cur != NULL)
|
||||
{
|
||||
next = cur->next;
|
||||
pcre_free_substring(cur->pattern);
|
||||
pcre_free(cur->left_re);
|
||||
pcre_free(cur->left_pe);
|
||||
pcre_free_substring(cur->right_str);
|
||||
free(cur);
|
||||
cur = next;
|
||||
}
|
||||
pthread_mutex_unlock(&self->lock);
|
||||
free(self);
|
||||
}
|
||||
|
||||
#endif
|
73
src/bin/network/atftpd/tftpd_pcre.h
Normal file
73
src/bin/network/atftpd/tftpd_pcre.h
Normal file
@ -0,0 +1,73 @@
|
||||
/* hey emacs! -*- Mode: C; c-file-style: "k&r"; indent-tabs-mode: nil -*- */
|
||||
/*
|
||||
* tftpd_pcre.h
|
||||
*
|
||||
* $Id: tftpd_pcre.h,v 1.1 2003/02/21 05:06:06 jp Exp $
|
||||
*
|
||||
* Copyright (c) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
|
||||
* and Remi Lefebvre <remi@debian.org>
|
||||
*
|
||||
* The PCRE code is provided by Jeff Miller <jeff.miller@transact.com.au>
|
||||
*
|
||||
* Copyright (c) 2003 Jeff Miller <jeff.miller@transact.com.au>
|
||||
*
|
||||
* atftp is free software; you can redistribute them and/or modify them
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef TFTPD_PCRE_H
|
||||
#define TFTPD_PCRE_H
|
||||
#include <pthread.h>
|
||||
#include <pcre.h>
|
||||
|
||||
#include "tftpd.h"
|
||||
|
||||
/* Carry out filename substitution
|
||||
* example, pattern
|
||||
* (name) sur($1)
|
||||
*
|
||||
* for the requested file "filename" would give "surname"
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* for when we read files, what is the format of a line
|
||||
* pattern [whitespace] replacement_string
|
||||
*/
|
||||
|
||||
#define TFTPD_PCRE_FILE_PATTERN "^(\\S+)\\s+(\\S+)$"
|
||||
|
||||
/*
|
||||
* Definition of struct to hold patterns
|
||||
*/
|
||||
|
||||
struct tftpd_pcre_pattern
|
||||
{
|
||||
unsigned int linenum;
|
||||
char *pattern;
|
||||
pcre *left_re;
|
||||
pcre_extra *left_pe;
|
||||
char *right_str;
|
||||
struct tftpd_pcre_pattern *next;
|
||||
};
|
||||
|
||||
typedef struct tftpd_pcre_pattern tftpd_pcre_pattern_t;
|
||||
|
||||
struct tftpd_pcre_self
|
||||
{
|
||||
pthread_mutex_t lock;
|
||||
char filename[MAXLEN];
|
||||
struct tftpd_pcre_pattern *list;
|
||||
};
|
||||
|
||||
typedef struct tftpd_pcre_self tftpd_pcre_self_t;
|
||||
|
||||
/* function prototypes */
|
||||
tftpd_pcre_self_t *tftpd_pcre_open(char *filename);
|
||||
char *tftpd_pcre_getfilename(tftpd_pcre_self_t *self);
|
||||
int tftpd_pcre_sub(tftpd_pcre_self_t *self, char *outstr, int outlen, char *str);
|
||||
void tftpd_pcre_close(tftpd_pcre_self_t *self);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user