Develop and Download Open Source Software

Browse Subversion Repository

Diff of /trunk/ttssh2/ttxssh/pkt.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

ttssh2/trunk/ttxssh/pkt.c revision 2942 by maya, Tue Dec 5 09:20:36 2006 UTC trunk/ttssh2/ttxssh/pkt.c revision 6985 by doda, Sat Nov 25 15:26:45 2017 UTC
# Line 1  Line 1 
1  /*  /*
2  Copyright (c) 1998-2001, Robert O'Callahan   * Copyright (c) 1998-2001, Robert O'Callahan
3  All rights reserved.   * (C) 2004-2017 TeraTerm Project
4     * All rights reserved.
5  Redistribution and use in source and binary forms, with or without modification,   *
6  are permitted provided that the following conditions are met:   * Redistribution and use in source and binary forms, with or without
7     * modification, are permitted provided that the following conditions
8  Redistributions of source code must retain the above copyright notice, this list of   * are met:
9  conditions and the following disclaimer.   *
10     * 1. Redistributions of source code must retain the above copyright
11  Redistributions in binary form must reproduce the above copyright notice, this list   *    notice, this list of conditions and the following disclaimer.
12  of conditions and the following disclaimer in the documentation and/or other materials   * 2. Redistributions in binary form must reproduce the above copyright
13  provided with the distribution.   *    notice, this list of conditions and the following disclaimer in the
14     *    documentation and/or other materials provided with the distribution.
15  The name of Robert O'Callahan may not be used to endorse or promote products derived from   * 3. The name of the author may not be used to endorse or promote products
16  this software without specific prior written permission.   *    derived from this software without specific prior written permission.
17     *
18  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND   * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
19  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  */   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28     */
29  /*  
30  This code is copyright (C) 1998-1999 Robert O'Callahan.  /*
31  See LICENSE.TXT for the license.  This code is copyright (C) 1998-1999 Robert O'Callahan.
32  */  See LICENSE.TXT for the license.
33    */
34  #include "ttxssh.h"  
35  #include "util.h"  #include "ttxssh.h"
36    #include "util.h"
37  //#define READAMOUNT 60000  #include "pkt.h"
38  // 60000 -> 65536 へ拡張。SSH2ではwindow制御を行うため、SSH2のwindow sizeと  
39  // 合わせておく必要がある。(2004.10.17 yutaka)  #define READAMOUNT CHAN_SES_WINDOW_DEFAULT
40  #define READAMOUNT 65536  
41    void PKT_init(PTInstVar pvar)
42  void PKT_init(PTInstVar pvar)  {
43  {          buf_create(&pvar->pkt_state.buf, &pvar->pkt_state.buflen);
44          buf_create(&pvar->pkt_state.buf, &pvar->pkt_state.buflen);          pvar->pkt_state.datastart = 0;
45          pvar->pkt_state.datastart = 0;          pvar->pkt_state.datalen = 0;
46          pvar->pkt_state.datalen = 0;          pvar->pkt_state.seen_server_ID = FALSE;
47          pvar->pkt_state.seen_server_ID = FALSE;          pvar->pkt_state.seen_newline = FALSE;
48          pvar->pkt_state.seen_newline = FALSE;          pvar->pkt_state.predecrypted_packet = FALSE;
49          pvar->pkt_state.predecrypted_packet = FALSE;  }
50  }  
51    /* Read some data, leave no more than up_to_amount bytes in the buffer,
52  /* Read some data, leave no more than up_to_amount bytes in the buffer,     return the number of bytes read or -1 on error or blocking. */
53     return the number of bytes read or -1 on error or blocking. */  static int recv_data(PTInstVar pvar, unsigned long up_to_amount)
54  static int recv_data(PTInstVar pvar, unsigned long up_to_amount)  {
55  {          int amount_read;
56          int amount_read;  
57            /* Shuffle data to the start of the buffer */
58          /* Shuffle data to the start of the buffer */          if (pvar->pkt_state.datastart != 0) {
59          if (pvar->pkt_state.datastart != 0) {                  memmove(pvar->pkt_state.buf,
60                  memmove(pvar->pkt_state.buf,                          pvar->pkt_state.buf + pvar->pkt_state.datastart,
61                                  pvar->pkt_state.buf + pvar->pkt_state.datastart,                          pvar->pkt_state.datalen);
62                                  pvar->pkt_state.datalen);                  pvar->pkt_state.datastart = 0;
63                  pvar->pkt_state.datastart = 0;          }
64          }  
65            buf_ensure_size(&pvar->pkt_state.buf, &pvar->pkt_state.buflen, up_to_amount);
66          buf_ensure_size(&pvar->pkt_state.buf, &pvar->pkt_state.buflen,  
67                                          up_to_amount);          _ASSERT(pvar->pkt_state.buf != NULL);
68    
69          _ASSERT(pvar->pkt_state.buf != NULL);          amount_read = (pvar->Precv) (pvar->socket,
70                                         pvar->pkt_state.buf + pvar->pkt_state.datalen,
71          amount_read = (pvar->Precv) (pvar->socket,                                       up_to_amount - pvar->pkt_state.datalen,
72                                                                   pvar->pkt_state.buf +                                       0);
73                                                                   pvar->pkt_state.datalen,  
74                                                                   up_to_amount - pvar->pkt_state.datalen,          if (amount_read > 0) {
75                                                                   0);                  /* Update seen_newline if necessary */
76                    if (!pvar->pkt_state.seen_server_ID && !pvar->pkt_state.seen_newline) {
77          if (amount_read > 0) {                          int i;
78                  /* Update seen_newline if necessary */  
79                  if (!pvar->pkt_state.seen_server_ID                          for (i = 0; i < amount_read; i++) {
80                          && !pvar->pkt_state.seen_newline) {                                  if (pvar->pkt_state.buf[pvar->pkt_state.datalen + i] == '\n') {
81                          int i;                                          pvar->pkt_state.seen_newline = 1;
82                                    }
83                          for (i = 0; i < amount_read; i++) {                          }
84                                  if (pvar->pkt_state.buf[pvar->pkt_state.datalen + i] ==                  }
85                                          '\n') {                  pvar->pkt_state.datalen += amount_read;
86                                          pvar->pkt_state.seen_newline = 1;          }
87                                  }  
88                          }          return amount_read;
89                  }  }
90    
91                  pvar->pkt_state.datalen += amount_read;  // 改行コードが出てくるまで読む
92          }  static int recv_line_data(PTInstVar pvar)
93    {
94          return amount_read;          int amount_read;
95  }          char buf[256];
96            size_t up_to_amount = sizeof(buf);
97            int i;
98  // 改行コードが出てくるまで読む  
99  static int recv_line_data(PTInstVar pvar)          /* Shuffle data to the start of the buffer */
100  {          if (pvar->pkt_state.datastart != 0) {
101          int amount_read;                  memmove(pvar->pkt_state.buf,
102          char buf[256];                          pvar->pkt_state.buf + pvar->pkt_state.datastart,
103          size_t up_to_amount = sizeof(buf);                          pvar->pkt_state.datalen);
104          int i;                  pvar->pkt_state.datastart = 0;
105            }
106          /* Shuffle data to the start of the buffer */  
107          if (pvar->pkt_state.datastart != 0) {          buf_ensure_size(&pvar->pkt_state.buf, &pvar->pkt_state.buflen, up_to_amount);
108                  memmove(pvar->pkt_state.buf,  
109                                  pvar->pkt_state.buf + pvar->pkt_state.datastart,          for (i = 0 ; i < (int)up_to_amount ; i++) {
110                                  pvar->pkt_state.datalen);                  amount_read = (pvar->Precv) (pvar->socket, &buf[i], 1, 0);
111                  pvar->pkt_state.datastart = 0;                  if (amount_read != 1) {
112          }                          return 0; // error
113                    }
114          buf_ensure_size(&pvar->pkt_state.buf, &pvar->pkt_state.buflen,  
115                                          up_to_amount);                  pvar->pkt_state.datalen += amount_read;
116    
117          for (i = 0 ; i < (int)up_to_amount ; i++) {                  if (buf[i] == '\n') { // 0x0a
118                  amount_read = (pvar->Precv) (pvar->socket,                          buf[i+1] = 0;
119                                                                          &buf[i],                          break;
120                                                                          1,                  }
121                                                                          0);          }
122                  if (amount_read != 1) {          amount_read = i + 1; // 読み込みサイズ(LFも含む)
123                          return 0; // error          memcpy(pvar->pkt_state.buf, buf, amount_read);
124                  }  
125            pvar->pkt_state.seen_newline = 1;
126                  pvar->pkt_state.datalen += amount_read;  
127            return amount_read;
128                  if (buf[i] == '\n') { // 0x0a  }
129                          buf[i+1] = 0;  
130                          break;  /* This function does two things:
131                  }     -- reads data from the sshd and feeds the SSH protocol packets to ssh.c
132          }     -- copies any available decrypted session data into the application buffer
133          amount_read = i + 1; // 読み込みサイズ(LFも含む)  */
134          memcpy(pvar->pkt_state.buf, buf, amount_read);  int PKT_recv(PTInstVar pvar, char *buf, int buflen)
135    {
136          pvar->pkt_state.seen_newline = 1;          int amount_in_buf = 0;
137            BOOL connection_closed = FALSE;
138          return amount_read;  
139  }          while (SSH_is_any_payload(pvar) ? buflen > 0 : !connection_closed) {
140                    if (SSH_is_any_payload(pvar)) {
141                            /* ssh.c has some session data for us to give to Tera Term. */
142  /* This function does two things:                          int grabbed = SSH_extract_payload(pvar, buf, buflen);
143     -- reads data from the sshd and feeds the SSH protocol packets to ssh.c  
144     -- copies any available decrypted session data into the application buffer                          amount_in_buf += grabbed;
145  */                          buf += grabbed;
146  int PKT_recv(PTInstVar pvar, char FAR * buf, int buflen)                          buflen -= grabbed;
147  {                  }
148          int amount_in_buf = 0;                  else if (!pvar->pkt_state.seen_server_ID && (pvar->pkt_state.seen_newline || pvar->pkt_state.datalen >= 255)) {
149          BOOL connection_closed = FALSE;                          /*
150                             * We're looking for the initial ID string and either we've seen the
151          while (SSH_is_any_payload(pvar) ? buflen > 0 : !connection_closed) {                           * terminating newline, or we've exceeded the limit at which we should see a newline.
152                  if (SSH_is_any_payload(pvar)) {                           */
153                          /* ssh.c has some session data for us to give to Teraterm. */                          unsigned int i;
154                          int grabbed = SSH_extract_payload(pvar, buf, buflen);  
155                            for (i = 0; pvar->pkt_state.buf[i] != '\n' && i < pvar->pkt_state.datalen; i++) {
156                          amount_in_buf += grabbed;                          }
157                          buf += grabbed;                          if (pvar->pkt_state.buf[i] == '\n') {
158                          buflen -= grabbed;                                  i++;
159                            }
160                  } else if (!pvar->pkt_state.seen_server_ID &&  
161                                     (pvar->pkt_state.seen_newline                          // SSHサーバのバージョンチェックを行う
162                                          || pvar->pkt_state.datalen >= 255)) {                          if (SSH_handle_server_ID(pvar, pvar->pkt_state.buf, i)) {
163                          /* We're looking for the initial ID string and either we've seen the                                  pvar->pkt_state.seen_server_ID = 1;
164                             terminating newline, or we've exceeded the limit at which we should see  
165                             a newline. */                                  if (SSHv2(pvar)) {
166                          unsigned int i;                                          // send Key Exchange Init
167                                            SSH2_send_kexinit(pvar);
168                          for (i = 0;                                  }
169                                   pvar->pkt_state.buf[i] != '\n'                          } else {
170                                   && i < pvar->pkt_state.datalen; i++) {                                  // reset flag to re-read server ID (2008.1.24 yutaka)
171                          }                                  pvar->pkt_state.seen_newline = 0;
172                          if (pvar->pkt_state.buf[i] == '\n') {                          }
173                                  i++;  
174                          }                          pvar->pkt_state.datastart += i;
175                            pvar->pkt_state.datalen -= i;
176                          // SSHサーバのバージョンチェックを行う                  }
177                          if (SSH_handle_server_ID(pvar, pvar->pkt_state.buf, i)) {                  else if (pvar->pkt_state.seen_server_ID && pvar->pkt_state.datalen >= SSH_get_min_packet_size(pvar)) {
178                                  pvar->pkt_state.seen_server_ID = 1;                          char *data = pvar->pkt_state.buf + pvar->pkt_state.datastart;
179                            uint32 padding;
180                                  if (SSHv1(pvar)) {                          uint32 pktsize;
181                            uint32 total_packet_size;
182                                  } else { // for SSH2(yutaka)                          struct Mac *mac = &pvar->ssh2_keys[MODE_IN].mac;
183                                          // send Key Exchange Init                          struct Enc *enc = &pvar->ssh2_keys[MODE_IN].enc;
184                                          SSH2_send_kexinit(pvar);                          int aadlen;
185                                  }  
186                            /*
187                          }                           * aadlen: Additional Authenticated Data Length
188                             *   - 暗号化しないが MAC や AEAD での認証の対象となるデータの長さ
189                          pvar->pkt_state.datastart += i;                           *
190                          pvar->pkt_state.datalen -= i;                           * EtM 方式の MAC や、AEAD な暗号ではパケットの先頭のパケット長部分は暗号化されず
191                             * 認証のみが行われる。パケット長は uint32 (4バイト) で格納されている。
192                  } else if (pvar->pkt_state.seen_server_ID                           * 通常の MAC 方式 (E&M) で、かつ AEAD でない暗号方式ではパケット長部分も暗号化
193                                     && pvar->pkt_state.datalen >=                           * されるので aadlen は 0 となる。
194                                     (unsigned int) SSH_get_min_packet_size(pvar)) {                           */
195                          char FAR *data =                          if (SSHv2(pvar) && ((mac && mac->etm) || (enc && enc->auth_len > 0))) {
196                                  pvar->pkt_state.buf + pvar->pkt_state.datastart;                                  aadlen = 4;
197                          uint32 padding;                          }
198                          uint32 pktsize;                          else {
199                          uint32 total_packet_size;                                  aadlen = 0;
200                            }
201                          //debug_print(10, data, pvar->pkt_state.datalen);  
202                            /*
203                          // SSH2なら暗号化パケットの一部を復号化する。                           * aadlen が 0 の時はパケット長部分が暗号化されている。パケット全体を受信してから
204                          if (!pvar->pkt_state.predecrypted_packet) {                           * 後段の処理を行う為にパケット長を知る必要が有る為、先頭の 1 ブロックを復号する。
205                                  //DEBUG_PRINT_TO_FILE(0, data, pvar->pkt_state.datalen);                           */
206                                  SSH_predecrpyt_packet(pvar, data);                          if (SSHv2(pvar) && !pvar->pkt_state.predecrypted_packet && aadlen == 0) {
207                                    SSH_predecrpyt_packet(pvar, data);
208                                  if (SSHv1(pvar)) {                                  pvar->pkt_state.predecrypted_packet = TRUE;
209                                          pvar->pkt_state.predecrypted_packet = TRUE;                          }
210                                  } else { // for SSH2(yutaka)  
211                                          // do nothing                          // パケットの先頭に uint32 (4バイト) のパケット長が来る
212                                          pvar->pkt_state.predecrypted_packet = TRUE;                          pktsize = get_uint32_MSBfirst(data);
213                                  }  
214                          }                          if (SSHv1(pvar)) {
215                                    // SSH1 ではパケット長の値には padding の長さが含まれていない。
216                          if (SSHv1(pvar)) {                                  // また padding の長さの情報もパケット上には無いので、パケット長の値から計算する。
217                                  uint32 realpktsize = get_uint32_MSBfirst(data);                                  padding = 8 - (pktsize % 8);
218    
219                                  padding = 8 - (realpktsize % 8);                                  // 以降の処理は pktsize に padding の値が含まれている事が前提となっている。
220                                  pktsize = realpktsize + padding;                                  pktsize += padding;
221                          } else {                          }
222                                  // SSH2のパケットは先頭に packet-size(4)+padding(1)+type(1) が続く。  
223                                  pktsize = get_uint32_MSBfirst(data);                          // パケット(TCPペイロード)の全体のサイズは、SSHペイロード+4(+MAC)となる。
224                                  padding = (unsigned char) data[4];                          // +4は、SSHペイロードのサイズを格納している部分(int型)。
225                          }                          total_packet_size = pktsize + 4 + SSH_get_authdata_size(pvar, MODE_IN);
226    
227                          // パケット(TCPペイロード)の全体のサイズは、SSHペイロード+4(+MAC)となる。                          if (total_packet_size <= pvar->pkt_state.datalen) {
228                          // +4は、SSHペイロードのサイズを格納している部分(int型)。                                  // 受信済みデータが十分有る場合はパケットの実処理を行う
229                          total_packet_size = pktsize + 4 + SSH_get_clear_MAC_size(pvar);                                  if (SSHv1(pvar)) {
230                                            // SSH1 は EtM 非対応 (そもそも MAC ではなく CRC を使う)
231                          if (total_packet_size <= pvar->pkt_state.datalen) {                                          SSH1_handle_packet(pvar, data, pktsize, padding);
232                                  /* the data must be 4 byte aligned. */                                  }
233                                  SSH_handle_packet(pvar, data, pktsize, padding);                                  else {
234                                  pvar->pkt_state.predecrypted_packet = FALSE;                                          // SSH2 ではこの時点では padding 長部分が復号されていない場合があるので、
235                                            // padding 長は渡さずに、必要になった時に内部で取得する。
236                                  pvar->pkt_state.datastart += total_packet_size;                                          SSH2_handle_packet(pvar, data, pktsize, aadlen, enc->auth_len);
237                                  pvar->pkt_state.datalen -= total_packet_size;                                  }
238    
239                          } else if (total_packet_size > 4 * 1024 * 1024) {                                  pvar->pkt_state.predecrypted_packet = FALSE;
240                                  // 4MBを超える巨大なパケットが届いたら、異常終了する。                                  pvar->pkt_state.datastart += total_packet_size;
241                                  // 実際にはデータ化けで復号失敗時に、誤認識することが多い。                                  pvar->pkt_state.datalen -= total_packet_size;
242  #ifdef I18N  
243                                  strcpy(pvar->ts->UIMsg, "Oversized packet received from server; connection will close.");                          }
244                                  UTIL_get_lang_msg("MSG_PKT_OVERSIZED_ERROR", pvar);                          else if (total_packet_size > PACKET_MAX_SIZE) {
245                                  notify_fatal_error(pvar, pvar->ts->UIMsg);                                  // パケット長が大きすぎる場合は異常終了する。
246  #else                                  // 実際には何らかの要因で復号失敗⇒パケット長部分が壊れている事が多い。
247                                  notify_fatal_error(pvar,                                  UTIL_get_lang_msg("MSG_PKT_OVERSIZED_ERROR", pvar,
248                                                                     "Oversized packet received from server; connection will close.");                                                    "Oversized packet received from server; connection will close.");
249  #endif                                  notify_fatal_error(pvar, pvar->ts->UIMsg, TRUE);
250                          } else {                          }
251                                  int amount_read =                          else {
252                                          recv_data(pvar, max(total_packet_size, READAMOUNT));                                  int amount_read = recv_data(pvar, max(total_packet_size, READAMOUNT));
253    
254                                  if (amount_read == SOCKET_ERROR) {                                  if (amount_read == SOCKET_ERROR) {
255                                          if (amount_in_buf == 0) {                                          if (amount_in_buf == 0) {
256                                                  return SOCKET_ERROR;                                                  return SOCKET_ERROR;
257                                          } else {                                          } else {
258                                                  return amount_in_buf;                                                  return amount_in_buf;
259                                          }                                          }
260                                  } else {                                  } else {
261                                          if (amount_read == 0) {                                          if (amount_read == 0) {
262                                                  connection_closed = TRUE;                                                  connection_closed = TRUE;
263                                          }                                          }
264                                  }                                  }
265                          }                          }
266                    } else {
267                            // パケットの受信
268                  } else {                          int amount_read;
269                          // パケットの受信(最大60KB)  
270                          int amount_read;                          amount_read = recv_data(pvar, READAMOUNT);
271    
272                          if (pvar->pkt_state.seen_server_ID == 0) {                          if (amount_read == SOCKET_ERROR) {
273                                  //amount_read = recv_line_data(pvar);                                  if (amount_in_buf == 0) {
274                                  amount_read = recv_data(pvar, READAMOUNT);                                          return SOCKET_ERROR;
275                                    } else {
276                          } else {                                          return amount_in_buf;
277                                  amount_read = recv_data(pvar, READAMOUNT);                                  }
278                            } else if (amount_read == 0) {
279                          }                                  connection_closed = TRUE;
280                            }
281                          if (amount_read == SOCKET_ERROR) {                  }
282                                  if (amount_in_buf == 0) {  
283                                          return SOCKET_ERROR;                  if (pvar->fatal_error) {
284                                  } else {                          return amount_in_buf;
285                                          return amount_in_buf;                  }
286                                  }          }
287                          } else if (amount_read == 0) {  
288                                  connection_closed = TRUE;          if (SSH_is_any_payload(pvar)) {
289                          }                  PostMessage(pvar->NotificationWindow, WM_USER_COMMNOTIFY, pvar->socket, MAKELPARAM(FD_READ, 0));
290                  }          }
291    
292                  if (pvar->fatal_error) {          return amount_in_buf;
293                          return amount_in_buf;  }
294                  }  
295          }  void PKT_end(PTInstVar pvar)
296    {
297          if (SSH_is_any_payload(pvar)) {          buf_destroy(&pvar->pkt_state.buf, &pvar->pkt_state.buflen);
298                  PostMessage(pvar->NotificationWindow, WM_USER_COMMNOTIFY,  }
                                         pvar->socket, MAKELPARAM(FD_READ, 0));  
         }  
   
         return amount_in_buf;  
 }  
   
 void PKT_end(PTInstVar pvar)  
 {  
         buf_destroy(&pvar->pkt_state.buf, &pvar->pkt_state.buflen);  
 }  
   
 /*  
  * $Log: not supported by cvs2svn $  
  * Revision 1.4  2006/11/23 02:19:30  maya  
  * 表示メッセージを言語ファイルから読み込みむコードの作成を開始した。  
  *  
  * Revision 1.3  2006/10/29 17:15:47  yutakapon  
  * DEBUG_PRINT_TO_FILEマクロを追加。  
  *  
  * Revision 1.2  2004/12/19 15:39:58  yutakakn  
  * CVS LogIDの追加  
  *  
  */  

Legend:
Removed from v.2942  
changed lines
  Added in v.6985

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26